/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 battlefield.cpp

	$Header: /heroes4/battlefield.cpp $

	$NoKeywords: $

 ************************************************************************/

#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "battlefield.h"

#include <cassert>
#include <map>
#include <math.h>
#include <set>
#include <utility>

#include "abstract_combat_ai_action.h"
#include "abstract_combat_object.h"
#include "abstract_tile.h"
#include "adaptor_handler.h"
#include "adventure_map.h"
#include "adventure_frame.h"
#include "angle.h"
#include "animated_window.h"
#include "animation_cache.h"
#include "army_ptr.h"
#include "artifact_prop_spell_charges.h"
#include "artifact_properties.h"
#include "artifact_slot.h"
#include "attackable_obstacle.h"
#include "battlefield_metrics.h"
#include "battlefield_preset_map.h"
#include "battlefield_terrain_map.h"
#include "battlefield_window.h"
#include "bitmap_group_cache.h"
#include "bitmap_group_window.h"
#include "bound_handler.h"
#include "building_traits.h"
#include "cached_grail_data_source.h"
#include "can_cast.h"
#include "castle_gate.h"
#include "circle_calculator.h"
#include "combat_actor.h"
#include "combat_actor_action.h"
#include "combat_actor_model.h"
#include "combat_actor_model_cache.h"
#include "combat_ai.h"
#include "combat_ai_ptr.h"
#include "combat_ai_action_ptr.h"
#include "combat_area_shader.h"
#include "combat_area_shader_data.h"
#include "combat_castle.h"
#include "combat_castle_ptr.h"
#include "combat_context.h"
#include "combat_creature.h"
#include "combat_flinch.h"
#include "combat_footprint.h"
#include "combat_label.h"
#include "combat_non_blocking_message_displayer.h"
#include "combat_object_model_cache.h"
#include "combat_object_type.h"
#include "combat_object_visitor.h"
#include "combat_obstacle_placer.h"
#include "combat_reader.h"
#include "combat_path_finder.h"
#include "combat_sounds.h"
#include "combat_spell.h"
#include "combat_spell_registration.h"
#include "combat_user_action.h"
#include "combat_window.h"
#include "compound_object.h"
#include "compound_object_model.h"
#include "convert_16_bit.h"
#include "creature.h"
#include "creature_ability.h"
#include "creature_ability_properties.h"
#include "creature_array.h"
#include "creature_traits.h"
#include "creature_type.h"
#include "dialog_combat_creature.h"
#include "direction.h"
#include "elements_of.h"
#include "enum_operations.h"
#include "external_string.h"
#include "format_string.h"
#include "function_handler.h"
#include "game_time.h"
#include "get_artifact_damage_modifier.h"
#include "hero.h"
#include "hero_keyword_replacer.h"
#include "idle_processor.h"
#include "isometric_map.h"
#include "iterator_operations.h"
#include "mana_leech.h"
#include "map_rect.h"
#include "missile_type.h"
#include "move_missile.h"
#include "music.h"
#include "open_gate_animation.h"
#include "options.h"
#include "ownable_garrisonable_adv_object.h"
#include "panic.h"
#include "platform.h"
#include "play_combat_animation.h"
#include "play_combat_flight.h"
#include "play_combat_walk.h"
#include "player.h"
#include "point_2d.h"
#include "popup_window.h"
#include "quad.h"
#include "random.h"
#include "rational.h"
#include "replace_keywords.h"
#include "report_combat_results.h"
#include "resurrect_heroes.h"
#include "run_combat.h"
#include "screen_point.h"
#include "scroll_menu.h"
#include "skill_properties.h"
#include "simple_dialog.h"
#include "sound.h"
#include "spell.h"
#include "spellbook_window.h"
#include "spell_effect_window.h"
#include "spell_properties.h"
#include "square.h"
#include "standard_fonts.h"
#include "stationary_combat_object.h"
#include "teleport_ability.h"
#include "terrain.h"
#include "text_window.h"
#include "threat_footprint.h"
#include "timed_window.h"
#include "town.h"
#include "town_image_level.h"

// -----------------------------------------------------------------------
// t_attack_angle_list members
// -----------------------------------------------------------------------
t_attack_angle const* t_attack_angle_list::get_closest() const
{
	t_attack_angle const* result = 0;
	const_iterator        index;

	for (index = begin(); index != end(); index++)
	{
		if (result == 0 || index->move_distance < result->move_distance)
			result = &(*index);
	}
	return result;
}

t_attack_angle const* t_attack_angle_list::get_farthest() const
{
	t_attack_angle const* result = 0;
	const_iterator        index;

	for (index = begin(); index != end(); index++)
	{
		if (result == 0 || index->move_distance > result->move_distance)
			result = &(*index);
	}
	return result;
}



// -----------------------------------------------------------------------
// Data-for-handler classes
// These classes basically exist to squeeze several more parameters into
// a maximum-of-3-arguments t_handler
// -----------------------------------------------------------------------
t_battlefield::t_missile_strike_data::t_missile_strike_data( bool physical_attack, t_combat_action_message const & message )
	: m_physical_attack( physical_attack ), m_message( message )
{
}

bool t_battlefield::t_missile_strike_data::get_is_physical_attack() const
{
	return m_physical_attack;
}

t_combat_action_message const & t_battlefield::t_missile_strike_data::get_message() const
{
	return m_message;
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
t_battlefield::t_end_damage_spell_data::t_end_damage_spell_data( t_combat_creature * creature, t_spell spell, t_combat_action_message const & message )
	: m_creature( creature ), m_spell( spell ), m_message( message )
{
}

t_combat_creature * t_battlefield::t_end_damage_spell_data::get_creature() const
{
	return m_creature;
}

t_spell t_battlefield::t_end_damage_spell_data::get_spell() const
{
	return m_spell;
}

t_combat_action_message const & t_battlefield::t_end_damage_spell_data::get_message() const
{
	return m_message;
}


// ***********************************************************************
//
// battlefield members
// 
// ***********************************************************************
extern t_animation_cache    const k_spell_cursor( "cursor.combat.cast_spell" );
static t_bitmap_group_cache const k_spell_cannot_cast_cursor( "cursor.combat.no_cast" );
static t_bitmap_group_cache const k_blocked_cursor( "cursor.blocked" );
static t_bitmap_group_cache const k_info_cursor( "cursor.combat.info" );
static t_bitmap_group_cache const k_normal_cursor( "cursor.combat.normal" );
static t_bitmap_group_cache const k_walk_cursor( "cursor.combat.walk" );
static t_bitmap_group_cache const k_fly_cursor( "cursor.combat.fly" );
static t_bitmap_group_cache const k_shoot_cursor( "cursor.combat.shoot" );
static t_bitmap_group_cache const k_tower_move_cursor( "cursor.combat.tower" );
static t_bitmap_group_cache const k_melee_cursors[16] =
{
	"cursor.combat.melee.northeast",
	"cursor.combat.melee.east_northeast",
	"cursor.combat.melee.east",
	"cursor.combat.melee.east_southeast",
	"cursor.combat.melee.southeast",
	"cursor.combat.melee.south_southeast",
	"cursor.combat.melee.south",
	"cursor.combat.melee.south_southwest",
	"cursor.combat.melee.southwest",
	"cursor.combat.melee.west_southwest",
	"cursor.combat.melee.west",
	"cursor.combat.melee.west_northwest",
	"cursor.combat.melee.northwest",
	"cursor.combat.melee.north_northwest",
	"cursor.combat.melee.north",
	"cursor.combat.melee.north_northeast",
};

// -----------------------------------------------------------------------
// get appropriate castle
// -----------------------------------------------------------------------
typedef t_pointer_cache< t_combat_castle > t_combat_castle_cache;

static t_combat_castle_ptr get_castle( t_town const* town )
{
	t_town_image_level level = town->get_castle_level();

	if (level == k_town_image_village)
		return t_combat_castle_ptr();

	static bool				     initialized = false;
	static t_combat_castle_cache caches[k_town_type_count][k_town_image_count - 1];

	if (!initialized)
	{
		t_town_type			type;
		t_town_image_level  index;
		std::string			name;

		initialized = true;
		for (type = t_town_type(0); type < k_town_type_count; enum_incr(type))
		{
			for (index = k_town_image_fort; index < k_town_image_count; enum_incr(index))
			{
				name = k_town_keyword[ type ];
				name += ".";
				name += get_town_image_keyword( index );
				caches[type][index - 1] = t_combat_castle_cache( name );
			}
		}
	}

	t_combat_castle_ptr result = caches[town->get_type()][level - 1].get();

	if (result.get() == 0)
		result = caches[k_town_order][level - 1].get();
	return result;
}

static t_combat_castle_cache const k_garrison_cache( "garrison.garrison" );
// -----------------------------------------------------------------------
// get models from castle
// -----------------------------------------------------------------------

static void get_castle_objects( t_combat_castle&					castle, 
							    t_combat_object_model_cache_array&  caches, double scale )
{
	int i;
	t_combat_castle_item_array const& objects = castle.get_objects();

	caches.clear();
	caches.reserve( objects.size() );
	for (i = 0; i < objects.size(); i++)
	{
		caches.push_back( get_combat_object_model_cache( objects[i].name, scale ) );
	}
}

// -----------------------------------------------------------------------
// flatten area under castle object
// -----------------------------------------------------------------------
static void flatten_area( t_compound_object_model const& model,
						  t_map_point_2d const&			 position,
						  t_battlefield_terrain_map&     map,
						  int							 height )
{
	t_map_point_2d footprint_size = model.get_footprint_size();
	t_map_rect_2d  rect;
	t_map_point_2d point;

	footprint_size <<= k_battlefield_subcell_shift;
	rect.top_left = position;
	rect.bottom_right = position + footprint_size;
	rect.top_left >>= k_battlefield_subcell_shift + k_battlefield_cell_shift;
	rect.bottom_right.column += k_battlefield_subcells_per_cell
		                        * k_battlefield_cells_per_terrain_tile - 1;
	rect.bottom_right.row += k_battlefield_subcells_per_cell
		                     * k_battlefield_cells_per_terrain_tile - 1;
	rect.bottom_right >>= k_battlefield_subcell_shift + k_battlefield_cell_shift;
	
	for (point.row = rect.top(); point.row <= rect.bottom(); point.row++)
	{
		for (point.column = rect.left(); point.column <= rect.right(); point.column++)
		{
			if (!map.is_valid( point ))
				continue;
			map.reset_height( point, height );
		}
	}
}


// -----------------------------------------------------------------------
// flatten area under castle objects
// -----------------------------------------------------------------------
typedef std::vector<t_compound_object_model_ptr> t_combat_object_model_array;

static void flatten_castle( t_combat_castle const&				castle,
						    t_combat_object_model_array const&	models,
							t_battlefield_terrain_map&			map, 
							int									height )
{
	int									i;
	t_combat_castle_item_array const&	objects = castle.get_objects();

	for (i = 0; i < objects.size(); i++)
		flatten_area( *models[i], objects[i].position, map, height );
}

// -----------------------------------------------------------------------
// mark terrain
// -----------------------------------------------------------------------
static void mark_as_grass( t_compound_object_model const& model,
						   t_map_point_2d const&		  position,
						   t_battlefield&				  battlefield )
{
	t_map_point_2d footprint_size = model.get_footprint_size();
	t_map_rect_2d  rect;
	t_map_point_2d point;

	footprint_size <<= k_battlefield_subcell_shift;
	rect.top_left = position;
	rect.bottom_right = position + footprint_size;
	rect.top_left >>= k_battlefield_subcell_shift;
	rect.bottom_right.column += k_battlefield_subcells_per_cell - 1;
	rect.bottom_right.row += k_battlefield_subcells_per_cell - 1;
	rect.bottom_right >>= k_battlefield_subcell_shift;
	
	for (point.row = rect.top(); point.row < rect.bottom(); point.row++)
	{
		for (point.column = rect.left(); point.column < rect.right(); point.column++)
		{
			if (!battlefield.is_valid( point ))
				continue;
			battlefield.get_cell( point ).set_terrain_type( k_terrain_grass, 0 );
		}
	}
}

// -----------------------------------------------------------------------
// find castle row
// -----------------------------------------------------------------------
static int find_castle_row( t_combat_castle const&		castle, 
					 t_combat_object_model_array const& models )
{
	int									castle_row = 0;
	int									i;
	t_combat_castle_item_array const&	objects = castle.get_objects();
	t_compound_object_model_ptr			model;
	t_map_point_2d                      position;
	int									bottom;

	castle_row = 0;
	for (i = 0; i < objects.size(); i++)
	{
		model = models[i];
		position = objects[i].position;
		bottom = model->get_footprint_size().row
				+ (position.row >> k_battlefield_subcell_shift);
		if (bottom > castle_row)
			castle_row = bottom;
	}
	return castle_row;
}

// -----------------------------------------------------------------------
// find castle row
// -----------------------------------------------------------------------
static int find_castle_inside_row( t_combat_castle const&		castle, 
					 t_combat_object_model_array const& models )
{
	int									castle_row = 0;
	int									i;
	t_combat_castle_item_array const&	objects = castle.get_objects();
	t_map_point_2d                      position;
	int									bottom;

	castle_row = 0;
	for (i = 0; i < objects.size(); i++)
	{
		position = objects[i].position;
		bottom = position.row >> k_battlefield_subcell_shift;
		if (bottom > castle_row)
			castle_row = bottom;
	}
	return castle_row;
}


// -----------------------------------------------------
// mark moat
// -----------------------------------------------------
static void draw_moat( t_battlefield_terrain_map& terrain_map, int row_start,
					   int row_end )
{
	t_map_point_2d	point;
	int				column_end;

	for (point.row = row_start; point.row < row_end; ++point.row)
	{
		point.column = terrain_map.get_tile_row_start( point.row );
		column_end = terrain_map.get_tile_row_end( point.row );
		for (; point.column < column_end; ++point.column)
		{
			t_battlefield_terrain_tile& tile = terrain_map.get_tile( point );

			tile.set_terrain( k_terrain_water_river );
			tile.set_terrain_subtype( 0 );
		}
	}
	terrain_map.set_river_height();
}

static t_external_string const k_text_damage_range_1( "text_damage_range_1" );
static t_external_string const k_text_damage_range_2( "text_damage_range_2" );

// ------------------------------------------------------------------------
// Produce standard damage range text for balloon help
// ------------------------------------------------------------------------
static std::string damage_range_text( int damage_low, int damage_high )
{
	std::string damage_text;

	if ( damage_low != damage_high )
	{
		damage_text = replace_keywords( k_text_damage_range_2, 
										"%damage_low", format_string( "%i", damage_low ),
										"%damage_high", format_string( "%i", damage_high ) );
	}
	else
	{
		damage_text = replace_keywords( k_text_damage_range_1, 
										"%damage", format_string( "%i", damage_low ) );
	}

	return damage_text;
}


t_battlefield::t_battlefield( t_creature_array* attacker, t_creature_array* defender )
{
	t_window* main_window = t_window::get_main_window();

	m_view_size_id = compute_view_size_id( main_window->get_client_rect().bottom_right() );
	m_armies[0] = attacker;
	m_armies[1] = defender;
	initialize();
}

// -----------------------------------------------------
// constructor
// -----------------------------------------------------
t_battlefield::t_battlefield( t_combat_context&					context,
							  t_screen_point const&				view_size, 
							  t_battlefield_terrain_map&		terrain_map, 
							  t_combat_window *					window,
							  t_preset_battle_map_array const&	battlefield_preset_map_vector,
							  bool								at_sea )
			 : 	m_view_size_id( compute_view_size_id( view_size ) ),
				m_cell_map( terrain_map.get_size() * k_battlefield_cells_per_terrain_tile,
						    k_battlefield_cell_height,
							k_battlefield_logical_view_size ),
				m_cell_vertex_map( terrain_map.get_size() * k_battlefield_cells_per_terrain_tile,
								   k_battlefield_cell_height,
								   k_battlefield_logical_view_size )
{
	int             i;
	t_map_point_2d  corner;
	t_town const*	town = context.get_town();

	m_armies[0] = context.get_attacker();
	m_armies[1] = context.get_defender();
	initialize();

	corner.row = 0;
	corner.column = terrain_map.get_tile_vertex_row_start( 0 );
	m_base_height = terrain_map.get_tile_vertex( corner ).get_height();
	m_castle_row = 0;
	m_castle_inside_row = 0;
	m_town = town;
	m_is_at_sea = at_sea;
	m_castle_level = context.get_defender()->get_castle_level();

	m_combat_window = window;

	// create castle objects
	t_combat_castle_ptr					castle;
	t_combat_object_model_cache_array	castle_caches;
	t_combat_object_model_array			castle_models;

	if (town != 0)
		castle = get_castle( town );
	else if (m_castle_level != k_town_image_village)
		castle = k_garrison_cache.get();
	if (castle.get() != 0)
	{
		get_castle_objects( *castle, castle_caches, get_model_scale() );
		for (i = 0; i < castle_caches.size(); ++i)
			castle_models.push_back( castle_caches[i].get() );
		flatten_castle( *castle, castle_models, terrain_map, m_base_height );
		m_castle_row = find_castle_row( *castle, castle_models );
		m_castle_inside_row = find_castle_inside_row( *castle, castle_models );
		if (m_castle_level >= k_town_image_citadel)
			draw_moat( terrain_map, (m_castle_row + 1) >> 1, ((m_castle_row + 1) >> 1) + 2 );
	}

	// create terrain map
	terrain_map.recalculate_vertex_lighting();
	initialize_cell_map( terrain_map );
	initialize_cell_vertex_map( terrain_map );
	if (castle.get() != 0)
	{
		place_castle( *castle, castle_caches );
	}

	//create the obstacle map
	set_preset_passability_maps( battlefield_preset_map_vector );
	set_forbidden_cells();

	t_combat_obstacle_placer obstacle_placer( *this, castle.get() != 0, m_castle_row );

	obstacle_placer.execute();
}

			
// -----------------------------------------------------
// constructor
// -----------------------------------------------------
void t_battlefield::initialize()
{
	int                      i;
	t_map_point_2d           corner;

	m_data_cache.initialize( *this );
	for (i = 0; i < 2; ++i)
	{
		m_players[i] = m_armies[i]->get_owner();
		m_auto_combat_flags[i] = false;
		m_fled[i] = false;
		m_morale_modifier[i] = 0;
		m_retreat_checked[i] = false;
	}
	m_castle_row = 0;
	m_castle_level = k_town_image_village;
	m_current_cursor_mode = k_combat_mode_ranged;
	m_last_was_defender = true;
	m_viewer_ptr = 0;
	m_turn_number = 0;
	m_combat_window = 0;
	m_is_at_sea = false;
	m_next_battlefield_id = 1;
	m_acting_creature = 0;
	m_acting_creature_reported = 0;
	m_town = 0;
	m_state = k_combat_state_selecting_creature;
	m_end_animation_handler = bound_handler( *this, &t_battlefield::end_animation );
	m_resolve_handler = bound_handler( *this, &t_battlefield::resolve_attacks );
	m_blocked_cursor.create( k_blocked_cursor.get() );
	m_normal_cursor.create( k_normal_cursor.get() );
	m_walk_cursor.create( k_walk_cursor.get() );
	m_fly_cursor.create( k_fly_cursor.get() );
	m_shoot_cursor.create( k_shoot_cursor.get() );
	m_defend_wait_displayer.reset( new t_combat_non_blocking_message_displayer( *this ) );
	for (i = 0; i < k_melee_cursor_count; i++)
	{
		m_melee_cursor[i].create( k_melee_cursors[i].get() );
	}
	m_info_cursor.create( k_info_cursor.get() );
	m_spell_cannot_cast_cursor.create( k_spell_cannot_cast_cursor.get() );
	m_tower_move_cursor.create( k_tower_move_cursor.get() );

	// create spell cursor
	t_window*       animated_window;
	t_screen_rect   rect;
	
	m_spell_cursor_animation = k_spell_cursor.get();
	rect = m_spell_cursor_animation->get_rect();
	m_spell_cursor = new t_mouse_window;
	m_spell_cursor->set_visible( false );
	m_spell_cursor->set_hot_spot( rect.size() / 2 );
	animated_window = new t_animated_window( m_spell_cursor_animation, t_screen_point(0,0), 100, m_spell_cursor );

	m_running_animations = 0;

	for (i = 0; i < 2; i++)
	{
		// Set up data cache for grail data, since it never changes during combat anyway.
		t_abstract_grail_data_source const& source = m_armies[i]->get_grail_data();
		
		m_grail_data[i].reset( new t_cached_grail_data_source( source ) );
	}

	m_sequence_loader.start();
}

t_battlefield::~t_battlefield()
{
	m_combat_window = NULL; // Combat window must have gone away before battlefield is destroyed,
							// since it holds a reference
	clear();
}

void t_battlefield::clear()
{
	t_combat_object_iterator index = objects_begin();
	t_combat_object_iterator end   = objects_end();

	// make these things destruct BEFORE we destroy battlefield.
	for (; index != end; index++)
	{
		(*index)->on_battlefield_destruction();
	}

	// clear out all data 
	m_attackable_obstacles.clear();
	m_attacks.clear();
	m_creature_list.clear();
	m_default_spell = 0;
	m_drift_windows.clear();
	m_fled_stacks.clear();
	m_gate = 0;
	m_last_target = 0;
	m_object_ptr_list.clear();
	m_pending_actions.clear();
	m_selected_creatures.clear();
	m_spell = 0;
	m_target = 0;
	m_wall_objects.clear();
	m_defend_wait_displayer->erase_action_message();
	m_current_movement_shaded_creature = NULL;

	while (!m_running_animation_list.empty())
	{
		m_running_animation_list.front()->animation_ended();
	}
}

// -----------------------------------------------------------------------
// place castle pieces
// -----------------------------------------------------------------------
void t_battlefield::place_castle( t_combat_castle const&					castle,
					              t_combat_object_model_cache_array const&	caches )
{
	int									i;
	int									j;
	t_combat_castle_item_array const&	objects = castle.get_objects();
	t_combat_object_model_cache			cache;
	t_compound_object_model_ptr			model;
	t_map_point_2d                      position;
	int									bottom;

	m_castle_row = 0;
	for (i = 0; i < objects.size(); i++)
	{
		cache = caches[i];
		model = cache.get();
		position = objects[i].position;
		bottom = model->get_footprint_size().row
				+ (position.row >> k_battlefield_subcell_shift);
		if (bottom > m_castle_row)
			m_castle_row = bottom;
		if (model->get_segments().size() == 1)
		{
			t_stationary_combat_object* obstacle;

			obstacle = new t_stationary_combat_object( *this, cache );
			if (!obstacle->is_underlay())
				m_wall_objects.push_back( obstacle );
			place_object( obstacle, position );
		}
		else
		{
			t_compound_object_ptr	obstacle;
			int						hits = 0;

			if (model->get_type() == k_obstacle_castle_gate)
			{
				m_gate = new t_castle_gate( *this, cache, m_town );
				obstacle = m_gate.get();
				m_gate->enable_animation( false );
			}
			else
			{
				obstacle = new t_compound_object( cache );
			}

			obstacle->place( *this, position );

			t_stationary_combat_object_array const& segments = obstacle->get_objects();

			for (j = 0; j < segments.size(); j++)
				m_wall_objects.push_back( segments[j] );
		}
		mark_as_grass( *caches[i].get(), objects[i].position, *this );
	}
}


// -----------------------------------------------------
// constructor
// -----------------------------------------------------

void t_battlefield::set_preset_passability_maps(std::vector<t_battlefield_preset_map_in_game_ptr> const& battlefield_preset_map_vector)
{
	t_combat_window*	combat_window = get_combat_window();
	t_combat_context*	context = combat_window->get_context();
	t_adv_map_point		location = context->get_location();
	bool				on_bridge = location.on_bridge;

	for (int i = 0; i < battlefield_preset_map_vector.size(); i++)
	{
		t_battlefield_passablity_map_ptr	passability_map = battlefield_preset_map_vector[i]->get_passability_map();
		t_map_point_2d						point;
		int									row_end;
		t_battlefield_cell*		b_cell_ptr;
		t_int8*					p_cell_ptr;

		if (m_cell_map.get_size() != passability_map->get_size()) 
			continue;

		point.row = m_cell_map.get_size();

		while (point.row--)
		{
			point.column	= m_cell_map.get_row_start( point.row );
			row_end			= m_cell_map.get_row_end( point.row );

			if (point.column !=		passability_map->get_row_start( point.row )		||
				row_end !=			passability_map->get_row_end( point.row ))
			{
				continue;
			}

			b_cell_ptr = &m_cell_map.get( point );
			p_cell_ptr = &passability_map->get( point );

			while (point.column < row_end)
			{
				switch (*p_cell_ptr)
				{
					case k_combat_passability_undefined:
						if (on_bridge)
						{
							b_cell_ptr->set_forbidden( true );
							b_cell_ptr->set_forbidden_by_preset( true );
							b_cell_ptr->set_obstacle_allowed( true );
						}
						break;

					case k_combat_passability_impassable:
						b_cell_ptr->set_forbidden( true );
						b_cell_ptr->set_forbidden_by_preset( true );
						b_cell_ptr->set_obstacle_allowed( false );
						break;
					
					case k_combat_passability_passable:
						b_cell_ptr->set_terrain_type( k_terrain_grass, 0 );
						b_cell_ptr->set_forbidden( false );
						b_cell_ptr->set_forbidden_by_preset( false );
						b_cell_ptr->set_obstacle_allowed( !on_bridge );
						break;

					case k_combat_passability_passable_no_obstacle:
						b_cell_ptr->set_terrain_type( k_terrain_grass, 0 );
						b_cell_ptr->set_forbidden( false );
						b_cell_ptr->set_forbidden_by_preset( false );
						b_cell_ptr->set_obstacle_allowed( false );
						break;
				}
				
				b_cell_ptr++;
				p_cell_ptr++;
				point.column++;

			}
		}
	}
}

void t_battlefield::set_forbidden_cells()
{
	t_map_point_2d      point;
	int                 row_end;
	t_battlefield_cell* ptr;

	point.row = m_cell_map.get_size();
	while (point.row--)
	{
		point.column = m_cell_map.get_row_start( point.row );
		row_end = m_cell_map.get_row_end( point.row );
		ptr = &m_cell_map.get( point );
		ptr->set_forbidden( true );
		while (point.column < row_end)
		{
			if (point.row + point.column <= 54)
				ptr->set_forbidden( true );
			ptr++;
			point.column++;
		}
		ptr[-1].set_forbidden( true );
	}
}

// -----------------------------------------------------------------------
// t_battlefield members
// -----------------------------------------------------------------------
void t_battlefield::add( t_combat_creature* actor )
{
	// attempt to move the object down the list
	t_combat_creature_list::iterator	index = m_creature_list.begin();
	t_combat_creature_list::iterator	end = m_creature_list.end();
	int									depth = actor->get_depth();

	for (; index != end; ++index )
	{
		int other_depth = index->get()->get_depth();

		if ( depth > other_depth )
			break;
	}
	m_creature_list.insert( index, actor );
}

// -----------------------------------------------------------------------
// move creature in creature list if drawing order has changed
// -----------------------------------------------------------------------
void t_battlefield::set_creature_drawing_order( t_combat_creature& creature )
{
	int									depth = creature.get_depth();
	t_combat_creature_list::iterator	index = m_creature_list.begin();
	t_combat_creature_list::iterator	end = m_creature_list.end();
	t_combat_creature_list::iterator	new_position;

	for (; index != end; ++index)
	{
		if (index->get() == &creature)
			break;
	}
	if (index == end)
		return;
	
	new_position = index;
	++new_position;
	m_creature_list.erase( index );

	t_combat_creature_list::iterator begin = m_creature_list.begin();

	while (new_position != begin)
	{
		--new_position;
		if (depth <= new_position->get()->get_depth())
		{
			++new_position;
			break;
		}
	}
	end = m_creature_list.end();
	for (; new_position != end; ++new_position)
	{
		if (depth >= new_position->get()->get_depth())
			break;
	}
	m_creature_list.insert( new_position, &creature );
}

// -----------------------------------------------------------------------
// t_battlefield members
// -----------------------------------------------------------------------
void t_battlefield::remove( t_combat_creature* actor )
{
	t_creature_list::iterator item = m_creature_list.begin();

	while (item != m_creature_list.end())
	{
		if (item->get() == actor)
		{
			m_creature_list.erase( item );
			return;
		}
		item++;
	}
}
// -----------------------------------------------------------------------
// t_battlefield members
// -----------------------------------------------------------------------
bool t_battlefield::object_hit_test( t_object const&		object,
									 t_uint32				current_time, 
									 t_screen_point const&	point ) const
{
	t_screen_point	object_base_point = subcell_to_screen_point( object.get_position() );

	return object.hit_test( current_time, point - object_base_point );
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
int t_battlefield::compute_terrain_height_under_object( t_object const& object ) const
{
	return compute_terrain_height_under_object( object, object.get_position() );
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
int t_battlefield::compute_terrain_height( t_map_point_2d const & subcell_point ) const
{
	// Compute the cell point and the subcell point relative to the cell
	t_map_point_2d cell_point = subcell_point >> k_battlefield_subcell_shift;
	t_map_point_2d relative_subcell_point = subcell_point & k_battlefield_subcell_mask;

	if (!is_valid( cell_point ))
		return m_base_height;

	// Do a bilinear interpolation of the vertex heights
	t_const_cell_vertex_quad vertex_quad = get_cell_vertex_quad( cell_point );
	int left_height = vertex_quad.left.get_height();
	int top_height = vertex_quad.top.get_height();
	int right_height = vertex_quad.right.get_height();
	int bottom_height = vertex_quad.bottom.get_height();

	int top_left_height =		top_height
							+		(	( left_height - top_height )
									*	relative_subcell_point.row )
								/	k_battlefield_subcells_per_cell;
	int bottom_right_height =		right_height
								+		(	( bottom_height - right_height )
										*	relative_subcell_point.row )
									/	k_battlefield_subcells_per_cell;

	return		top_left_height
			+		(	( bottom_right_height - top_left_height ) 
					*	relative_subcell_point.column )
				/	k_battlefield_subcells_per_cell;
}

int	t_battlefield::compute_terrain_height( t_map_point_2d const& subcell,
		                                   t_map_point_2d const& footprint ) const
{
	t_map_point_2d	center_subcell_point = subcell;
	
	center_subcell_point += (footprint >> 1);
	return compute_terrain_height( center_subcell_point );
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
int t_battlefield::compute_terrain_height_under_object(
	t_object const &		object,
	t_map_point_2d const &	subcell_point ) const
{
	int half_footprint_subcell_size =
		object.get_footprint_size() << ( k_battlefield_subcell_shift - 1 );
	
	t_map_point_2d	center_subcell_point(
						subcell_point.row + half_footprint_subcell_size,
						subcell_point.column + half_footprint_subcell_size );

	return compute_terrain_height( center_subcell_point );
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
t_battlefield::t_cell_vertex_quad
t_battlefield::get_cell_vertex_quad( t_map_point_2d const & cell_point )
{
	t_level_map_point_2d work_point( cell_point, 0 );
	t_cell_vertex & top = m_cell_vertex_map.get( work_point );
	++work_point.row;
	t_cell_vertex & left = m_cell_vertex_map.get( work_point );
	++work_point.column;
	t_cell_vertex & bottom = m_cell_vertex_map.get( work_point );
	--work_point.row;
	t_cell_vertex & right = m_cell_vertex_map.get( work_point );

	return t_cell_vertex_quad( left, top, right, bottom );
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
t_battlefield::t_const_cell_vertex_quad
t_battlefield::get_cell_vertex_quad( t_map_point_2d const & cell_point ) const
{
	t_level_map_point_2d work_point( cell_point, 0 );
	t_cell_vertex const & top = m_cell_vertex_map.get( work_point );
	++work_point.row;
	t_cell_vertex const & left = m_cell_vertex_map.get( work_point );
	++work_point.column;
	t_cell_vertex const & bottom = m_cell_vertex_map.get( work_point );
	--work_point.row;
	t_cell_vertex const & right = m_cell_vertex_map.get( work_point );

	return t_const_cell_vertex_quad( left, top, right, bottom );
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
t_rational_point const & t_battlefield::get_view_ratio() const
{
	return get_battlefield_view_size( m_view_size_id ).view_ratio;
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
t_screen_point const & t_battlefield::get_view_size() const
{
	return get_battlefield_view_size( m_view_size_id ).size;
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
void t_battlefield::initialize_cell_map(
	t_battlefield_terrain_map const & terrain_map )
{
	// Initialize the cell map
	int cell_map_size = m_cell_map.get_size();
	t_map_point_2d cell_point;
	for ( cell_point.row = 0; cell_point.row < cell_map_size; ++cell_point.row )
	{
		int row_start = m_cell_map.get_row_start( cell_point.row );
		int row_end = m_cell_map.get_row_end( cell_point.row );
		for ( cell_point.column = row_start; cell_point.column < row_end; ++cell_point.column )
		{
			t_map_point_2d terrain_tile_point = cell_point >> k_battlefield_cell_shift;
			t_battlefield_terrain_map::t_tile const & terrain_tile =
				terrain_map.get_tile( terrain_tile_point );

			m_cell_map.get( t_level_map_point_2d( cell_point, 0 ) ).set_terrain_type(
				terrain_tile.get_terrain(),
				terrain_tile.get_terrain_subtype() );
		}
	}
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
void t_battlefield::initialize_cell_vertex_map(
	t_battlefield_terrain_map const & terrain_map )
{
	// The following code assumes that the battlefield cells are half the
	// width and height of the terrain tiles
	assert( k_battlefield_cells_per_terrain_tile == 2 );

	int vertex_map_size = m_cell_vertex_map.get_size();
	
	t_map_point_2d vertex_point;
	t_map_point_2d terrain_vertex_point;

	// First initialize all of the even vertex rows.  These rows correspond
	// with the vertex rows on the terrain map
	for (	vertex_point.row = 0, terrain_vertex_point.row = 0;
			vertex_point.row < vertex_map_size;
			vertex_point.row += 2, ++terrain_vertex_point.row )
	{
		int row_start = m_cell_vertex_map.get_row_start( vertex_point.row );
		int row_end = m_cell_vertex_map.get_row_end( vertex_point.row );

		// First initialize all of the vertices which correspond exactly with
		// vertices on the terrain map

		// Make sure we start on an even column
		vertex_point.column = row_start;
		if ( ( vertex_point.column & 1 ) != 0 )
			++vertex_point.column;
		for (	terrain_vertex_point.column = vertex_point.column >> 1;
				vertex_point.column < row_end;
				vertex_point.column += 2, ++terrain_vertex_point.column )
		{
			int terrain_vertex_height = terrain_map.get_tile_vertex( terrain_vertex_point ).get_height();
			m_cell_vertex_map.get( t_level_map_point_2d( vertex_point, 0 ) ).set_height( terrain_vertex_height );
		}

		// Now initialize the vertices which lie on the edges between the
		// vertices on the terrain map along this row

		// Make sure we start on an odd column
		vertex_point.column = row_start;
		if ( ( vertex_point.column & 1 ) == 0 )
			++vertex_point.column;
		for (	terrain_vertex_point.column = vertex_point.column >> 1;
				vertex_point.column < row_end;
				vertex_point.column += 2, ++terrain_vertex_point.column )
		{
			int height_sum = terrain_map.get_tile_vertex( terrain_vertex_point ).get_height();

			t_map_point_2d next_terrain_vertex_point( terrain_vertex_point.row, terrain_vertex_point.column + 1 );
			height_sum += terrain_map.get_tile_vertex( next_terrain_vertex_point ).get_height();

			m_cell_vertex_map.get( t_level_map_point_2d( vertex_point, 0 ) ).set_height( ( height_sum + 1 ) / 2 );
		}
	}

	// Now initialize all of the odd vertex rows.  These rows lie between
	// the vertex rows on the terrain map
	for (	vertex_point.row = 1, terrain_vertex_point.row = 0;
			vertex_point.row < vertex_map_size;
			vertex_point.row += 2, ++terrain_vertex_point.row )
	{
		int row_start = m_cell_vertex_map.get_row_start( vertex_point.row );
		int row_end = m_cell_vertex_map.get_row_end( vertex_point.row );

		// First initialize the vertices which lie on the edges between the
		// terrain map vertices

		// Make sure we start on an even column
		vertex_point.column = row_start;
		if ( ( vertex_point.column & 1 ) != 0 )
			++vertex_point.column;
		for (	terrain_vertex_point.column = vertex_point.column >> 1;
				vertex_point.column < row_end;
				vertex_point.column += 2, ++terrain_vertex_point.column )
		{
			int height_sum = terrain_map.get_tile_vertex( terrain_vertex_point ).get_height();

			t_map_point_2d next_terrain_vertex_point( terrain_vertex_point.row + 1, terrain_vertex_point.column );
			height_sum += terrain_map.get_tile_vertex( next_terrain_vertex_point ).get_height();

			m_cell_vertex_map.get( t_level_map_point_2d( vertex_point, 0 ) ).set_height( ( height_sum + 1 ) / 2 );
		}

		// Now initialize the vertices which lie in the center of the terrain
		// tiles

		// Make sure we start on an odd column
		vertex_point.column = row_start;
		if ( ( vertex_point.column & 1 ) == 0 )
			++vertex_point.column;
		for (	terrain_vertex_point.column = vertex_point.column >> 1;
				vertex_point.column < row_end;
				vertex_point.column += 2, ++terrain_vertex_point.column )
		{
			t_quad< t_battlefield_terrain_map::t_tile_vertex const & > terrain_vertex_quad =
				terrain_map.get_tile_vertex_quad( terrain_vertex_point );

			int height_sum =		terrain_vertex_quad.top.get_height()
								+	terrain_vertex_quad.left.get_height()
								+	terrain_vertex_quad.bottom.get_height()
								+	terrain_vertex_quad.right.get_height();

			m_cell_vertex_map.get( t_level_map_point_2d( vertex_point, 0 ) ).set_height( ( height_sum + 2 ) / 4 );
		}
	}
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
void t_battlefield::on_idle()
{
	if (m_state != k_combat_state_awaiting_human_order)
		return;

	// pick someone at random to fidget.
	int total = 0;
	t_creature_list::iterator index;

	for (index = m_creature_list.begin(); index != m_creature_list.end(); index++)
	{
		if ((*index)->get_number() > 0 
			&& (*index)->get_current_action() == k_combat_actor_action_wait
			&& !(*index)->is_active( k_spell_frozen))
			total++;
	}
	if (total == 0)
	{
		set_delay( 500 );
		return;
	}
	total = random( total );
	for (index = m_creature_list.begin(); index != m_creature_list.end(); index++)
	{
		if ((*index)->get_number() > 0 
			&& (*index)->get_current_action() == k_combat_actor_action_wait
			&& !(*index)->is_active( k_spell_frozen))
		{
			total--;
			if (total < 0)
				break;
		}
	}

	t_combat_creature& actor = **index;
	t_uint32           delay;

	actor.set_animation( k_combat_actor_action_fidget );
	delay = actor.get_frame_count( k_combat_actor_action_fidget ) 
	        * 1000 / actor.get_frames_per_second();
	delay += random( 1000, 3000 );
	set_delay( delay );
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
void t_battlefield::invalidate( t_object& object ) const
{
	// Notify all of the viewers of the imminent move
	if (m_viewer_ptr != 0)
		m_viewer_ptr->invalidate_object( object );
}

// -----------------------------------------------------------------------
// stamp object on battlefield
// -----------------------------------------------------------------------
void t_battlefield::stamp( t_object* object, t_combat_footprint const& footprint,
						   t_map_point_2d const& point )
{
	int                       footprint_size = footprint.get_size();
	t_map_point_2d            offset;
	t_map_point_2d            new_point;
	int const*				  ptr = footprint.get();

	for (offset.row = 0; offset.row < footprint_size; offset.row++)
	{
		offset.column = 0;
		for (offset.column = 0; offset.column < footprint_size; offset.column++)
		{
			if (!(*(ptr++)))
				continue;
			new_point = point + offset;
			if (is_valid( new_point ))
				m_cell_map.get( new_point ).add( object );
		}
	}
}

// -----------------------------------------------------------------------
// unstamp object on battlefield
// -----------------------------------------------------------------------
void t_battlefield::unstamp( t_object* object, t_combat_footprint const& footprint,
							 t_map_point_2d const& point )
{
	int                       footprint_size = footprint.get_size();
	t_map_point_2d            offset;
	t_map_point_2d            new_point;
	int const*				  ptr = footprint.get();

	for (offset.row = 0; offset.row < footprint_size; offset.row++)
	{
		offset.column = 0;
		for (offset.column = 0; offset.column < footprint_size; offset.column++)
		{
			if (!(*(ptr++)))
				continue;
			new_point = point + offset;
			if (m_cell_map.is_valid( new_point ))
				m_cell_map.get( new_point ).remove( object );
		}
	}
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
void t_battlefield::place_object( t_object_ptr			object_ptr,
								  t_map_point_3d const &	position )
{
	object_ptr->move( *this, position );
	assert( object_ptr->get_position() == position );
	assert( object_ptr->get_list_position()._Mynode() == NULL );

	// Insert this object sorted by depth
	int						 object_depth	 = object_ptr->get_depth();
	t_combat_object_iterator object_ptr_iter = m_object_ptr_list.begin();

	for ( ; object_ptr_iter != m_object_ptr_list.end(); ++object_ptr_iter )
	{
		int other_object_depth = object_ptr_iter->get()->get_depth();
		if ( object_depth >= other_object_depth )
			break;
	}

	object_ptr_iter = m_object_ptr_list.insert( object_ptr_iter, object_ptr );
	object_ptr->set_list_position( object_ptr_iter );
	stamp( object_ptr, object_ptr->get_footprint(), object_ptr->get_cell_position() );
	object_ptr->on_placed();
	invalidate( *object_ptr );
	if (object_ptr->blocks_movement())
		m_data_cache.clear_paths();
}

// -----------------------------------------------------------------------
// set drawing order for an object
// -----------------------------------------------------------------------
void t_battlefield::set_drawing_order( t_object& object )
{
	assert( object.get_list_position()._Mynode() != NULL );

	int new_depth = object.get_depth();

	// Save the an iterator to the next element in the list
	t_object_iterator        object_iter = object.get_list_position();
	t_combat_object_iterator next_object_ptr_iter = next_iterator( object_iter );

	// Remove our element from the list an save it in a temporary list
	t_object_ptr_list temp_list;
	temp_list.splice( temp_list.end(), m_object_ptr_list, object_iter );

	// attempt to move the object up the list
	while ( next_object_ptr_iter != m_object_ptr_list.begin() )
	{
		int other_depth = prior_iterator( next_object_ptr_iter )->get()->get_depth();

		if ( new_depth < other_depth )
			break;
		--next_object_ptr_iter;
	}

	// attempt to move the object down the list
	while ( next_object_ptr_iter != m_object_ptr_list.end() )
	{
		int other_depth = next_object_ptr_iter->get()->get_depth();
		if ( new_depth > other_depth )
			break;
		++next_object_ptr_iter;
	}

	// Move the element back into the object list
	m_object_ptr_list.splice( next_object_ptr_iter, temp_list, temp_list.begin() );
	invalidate( object );
}

// -----------------------------------------------------------------------
// change a creature's model
// -----------------------------------------------------------------------
void t_battlefield::change_model( t_combat_creature&					creature,
								  t_cached_ptr< t_combat_actor_model >	model )
{
	assert( creature.get_list_position()._Mynode() != NULL );


	invalidate( creature );

	int old_footprint_size = creature.get_footprint_size();
	int new_footprint_size = model->get_footprint_size();

	if (old_footprint_size != new_footprint_size)
	{
		t_map_point_3d	position = creature.get_footprint_center();
		int				change = new_footprint_size << (k_battlefield_subcell_shift - 1);

		position.row -= change;
		position.column -= change;
		// round to cell
		position.row &= ~k_battlefield_subcell_mask;
		position.column &= ~k_battlefield_subcell_mask;
		position.height = compute_terrain_height_under_object( creature, position );

		creature.on_moving();
		unstamp( &creature, creature.get_footprint(), creature.get_cell_position() );
		creature.move( *this, position );
		set_drawing_order( creature );
	}
	creature.set_model( model );
	if (old_footprint_size != new_footprint_size)
	{
		stamp( &creature, creature.get_footprint(), creature.get_cell_position() );
		creature.on_moved( true );
	}
	invalidate( creature );
	m_data_cache.clear_paths();
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
void t_battlefield::move_object( t_object&				 object,
								 t_map_point_3d const &	 new_position )
{
	assert( object.get_list_position()._Mynode() != NULL );
	if ( new_position == object.get_position() )
		return;

	t_map_point_2d cell_position    = object.get_cell_position();
	t_map_point_2d new_cell_position = new_position;

	new_cell_position >>= k_battlefield_subcell_shift;
	invalidate( object );
	if (new_cell_position != cell_position)
	{
		object.on_moving();
		unstamp( &object, object.get_footprint(), object.get_cell_position() );
		if (new_position.height != object.get_position().height)
		{
			int height;

			height = compute_terrain_height_under_object( object, new_position );
			object.set_shadow_displacement( new_position.height - height, *this );
		}
	}
	object.move( *this, new_position );
	set_drawing_order( object );
	if (new_cell_position != cell_position)
		stamp( &object, object.get_footprint(), object.get_cell_position() );
	object.on_moved( new_cell_position != cell_position);
	invalidate( object );
	m_data_cache.clear_paths();
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
void t_battlefield::remove_object( t_object_ptr object_ptr )
{
	if (object_ptr->get_list_position()._Mynode() == NULL)
		return;

	// Simply take it out of the list
	m_object_ptr_list.erase( object_ptr->get_list_position() );
	object_ptr->set_list_position( t_combat_object_iterator() );
	unstamp( object_ptr, object_ptr->get_footprint(), object_ptr->get_cell_position() );
	object_ptr->on_removed();
	invalidate( *object_ptr );
	if (object_ptr->blocks_movement())
		m_data_cache.clear_paths();
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
void t_battlefield::set_current_action( t_actor&				 actor,
										t_combat_actor_action_id new_action_id,
										int						 new_frame_num )
{
	assert( new_action_id >= 0 && new_action_id < k_combat_actor_action_count );

	if (	new_action_id == actor.get_current_action()
		&&	new_frame_num == actor.get_current_frame_num() )
		return;

	invalidate( actor );
	actor.set_current_action( new_action_id, new_frame_num );
	invalidate( actor );
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
void t_battlefield::set_current_direction( 	t_actor&	actor,
											t_direction	new_direction )
{
	assert( new_direction >= 0 && new_direction < k_direction_count );

	if ( new_direction == actor.get_current_direction() )
		return;

	invalidate( actor );
	actor.on_turning();
	actor.set_current_direction( new_direction );
	actor.on_turned();
	invalidate( actor );
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
void t_battlefield::set_current_direction_and_action(
	t_actor&				 actor,
	t_direction				 new_direction,
	t_combat_actor_action_id new_action_id,
	int						 new_frame_num )
{
	assert( new_direction >= 0 && new_direction < k_direction_count );
	assert( new_action_id >= 0 && new_action_id < k_combat_actor_action_count );

	if (	new_direction == actor.get_current_direction()
		&&	new_action_id == actor.get_current_action()
		&&	new_frame_num == actor.get_current_frame_num() )
		return;

	invalidate( actor );
	actor.on_turning();
	actor.set_current_direction( new_direction );
	actor.set_current_action( new_action_id, new_frame_num );
	actor.on_turned();
	invalidate( actor );
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
void t_battlefield::set_current_frame_num( t_actor& actor,
										   int		 new_frame_num )
{
	if ( new_frame_num == actor.get_current_frame_num() )
		return;

	invalidate( actor );
	actor.set_current_frame_num( new_frame_num );
	invalidate( actor );
}

// -----------------------------------------------------------------------
// t_battlefield members
// -----------------------------------------------------------------------

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
bool t_battlefield::raw_cell_hit_test( t_rational_point const & logical_point, 
								   t_map_point_2d & result ) const
{
	static t_rational< int > const k_cell_width = k_battlefield_cell_width;
	static t_rational< int > const k_half_cell_width = k_cell_width / 2;
	static t_rational< int > const k_cell_height = k_battlefield_cell_height;
	static t_rational< int > const k_half_cell_height = k_cell_height / 2;

	// Compute the origin of the cell map's view rect relative to the origin of the cell map
	int				cell_map_size			= m_cell_map.get_size();
	int				cell_map_pixel_height	= cell_map_size * k_battlefield_cell_height;
	t_screen_point	cell_map_view_origin(
						-( k_battlefield_logical_view_width / 2 ),
						( cell_map_pixel_height - k_battlefield_logical_view_height ) / 2 
						- m_base_height );

	// Compute the coordinates of the cell under the logical view point without
	// considering vertex heights
	t_rational< int > x_div_width
		= ( logical_point.x + cell_map_view_origin.x ) / k_cell_width;
	t_rational< int > y_div_height 
		= ( logical_point.y + cell_map_view_origin.y ) / k_cell_height;
	t_map_point_2d	  cell_point(
						round_down( y_div_height - x_div_width ),
						round_down( y_div_height + x_div_width ) );

	if ( !m_cell_map.is_valid( t_level_map_point_2d( cell_point, 0 ) ) )
		return false;

	// Now we have to scan downward through the cells to determine which cell
	// the specified point is actually in.

	// Compute the base screen point of this cell
	t_screen_point	cell_screen_point =	convert_map_point_to_screen(
											cell_point,
											k_battlefield_cell_height,
											k_battlefield_logical_view_size,
											m_cell_map.get_size() );

	t_rational< int > relative_x;
	t_map_point_2d side_vertex_point = cell_point;
	bool side_is_left;

	if ( logical_point.x >= cell_screen_point.x )
	{
		relative_x = ( cell_screen_point.x + k_half_cell_width ) - logical_point.x;
		++side_vertex_point.column;
		side_is_left = false;
	}
	else
	{
		relative_x = logical_point.x - ( cell_screen_point.x - k_half_cell_width );
		++side_vertex_point.row;
		side_is_left = true;
	}
	t_cell_vertex const & side_vertex = get_cell_vertex( side_vertex_point );
	int side_y = cell_screen_point.y + k_battlefield_cell_height / 2 - side_vertex.get_height()
		         + m_base_height;

	for (;;)
	{
		if ( !m_cell_map.is_valid( t_level_map_point_2d( cell_point, 0 ) ) )
			return false;

		t_cell_vertex const & bottom_vertex = get_cell_vertex( cell_point + t_map_point_2d( 1, 1 ) );
		int bottom_y = cell_screen_point.y + k_battlefield_cell_height
			           - bottom_vertex.get_height() + m_base_height;

		t_rational< int > edge_y = side_y + ( bottom_y - side_y ) * relative_x / k_half_cell_width;
		if ( logical_point.y < edge_y )
			break;

		if ( side_is_left )
			++cell_point.row;
		else
			++cell_point.column;
		side_is_left = !side_is_left;
		side_y = bottom_y;
		relative_x = k_half_cell_width - relative_x;
		cell_screen_point.y += k_battlefield_cell_height / 2;
	}

	result = cell_point;
	return true;
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
t_screen_point t_battlefield::cell_to_screen_point( t_map_point_2d const & point ) const
{
	t_screen_point result =	convert_map_point_to_screen(
								point,
								k_battlefield_cell_height,
								k_battlefield_logical_view_size,
								m_cell_map.get_size() );
	t_rational_point const & view_ratio = get_view_ratio();
	result.x = round_to_nearest( result.x * view_ratio.x );
	result.y = round_to_nearest( result.y * view_ratio.y );

	return result;
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
t_screen_point t_battlefield::cell_to_screen_point( t_map_point_3d const & point ) const
{
	t_screen_point result =	convert_map_point_to_screen(
								point,
								k_battlefield_cell_height,
								k_battlefield_logical_view_size,
								m_cell_map.get_size() );
	t_rational_point const & view_ratio = get_view_ratio();
	result.x = round_to_nearest( result.x * view_ratio.x );
	result.y = round_to_nearest( ( result.y - point.height + m_base_height ) * view_ratio.y );

	return result;
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
double t_battlefield::get_model_scale() const
{
	return double( get_view_size().x ) / k_battlefield_logical_view_width;
}


// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
/*
t_screen_point t_battlefield::subcell_to_screen_point( t_map_point_2d const & point ) const
{
	t_screen_point result =	convert_map_point_to_screen(
								point,
								k_battlefield_subcell_height,
								k_battlefield_logical_view_size,
								m_cell_map.get_size() * k_battlefield_subcells_per_cell );
	t_rational_point const & view_ratio = get_view_ratio();
	result.x = round_to_nearest( result.x * view_ratio.x );
	result.y = round_to_nearest( result.y * view_ratio.y );

	return result;
}
*/

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
t_screen_point t_battlefield::subcell_to_screen_point( t_map_point_3d const & point ) const
{
	t_screen_point result =	convert_map_point_to_screen(
								point,
								k_battlefield_subcell_height,
								k_battlefield_logical_view_size,
								m_cell_map.get_size() * k_battlefield_subcells_per_cell );
	t_rational_point const & view_ratio = get_view_ratio();
	result.x = round_toward_zero( result.x * view_ratio.x );
	result.y = round_toward_zero( ( result.y - point.height + m_base_height ) * view_ratio.y );

	return result;
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
t_combat_object_iterator t_battlefield::object_hit_test( t_uint32				current_time,
														 t_screen_point const &	screen_point )
{
	t_object_iterator end	= m_object_ptr_list.end();
	t_object_iterator result = m_object_ptr_list.begin();

	for ( ; result != end; ++result )
	{
		if ( object_hit_test( **result, current_time, screen_point ) )
			break;
	}
	return result;
}

bool t_battlefield::cell_hit_test( t_screen_point const& screen_point,
									t_map_point_2d&		 cell_point ) const
{
	// first, check for any towers
	t_uint32										current_time = get_time();
	t_combat_object_list const&						list = m_viewer_ptr->get_objects( screen_point );
	t_combat_object_list::const_reverse_iterator	index;
	t_combat_object_list::const_reverse_iterator	end;
	t_stationary_combat_object const*				object;

	end = list.rend();
	index = list.rbegin();
	for ( ; index != end; ++index )
	{
		object = dynamic_cast<t_stationary_combat_object const*>(index->get());
		if (object == 0)
			continue;
		if (object->get_obstacle_type() != k_obstacle_castle_tower)
			continue;
		if ( !object_hit_test( **index, current_time, screen_point ) )
			continue;
		// return tower's center
		cell_point = object->get_footprint_center() >> k_battlefield_subcell_shift;
		return true;
	}

	// do normal cell hit test
	return raw_cell_hit_test( screen_point, cell_point );
}

// -----------------------------------------------------------------------
// get creature at cursor position.  Uses footprint first, then creature outline.
// -----------------------------------------------------------------------
t_abstract_combat_object* t_battlefield::attackable_hit_test( t_screen_point const& screen_point ) const
{
	t_map_point_2d							point;
	t_combat_object_list::const_reverse_iterator index;
	t_combat_object_list::const_reverse_iterator end;

	// first, is the object underneath the cursor a tower?

	if (cell_hit_test( screen_point, point ))
	{
		t_abstract_combat_object* result = m_cell_map.get( point ).get_attackable_object();

		if (result != 0 && result->can_be_attacked())
			return result;
	}

	t_uint32 current_time = get_time();

	end = m_object_ptr_list.rend();
	index = m_object_ptr_list.rbegin();
	for ( ; index != end; ++index )
	{
		if (!index->get()->can_be_attacked())
			continue;
		if ( object_hit_test( **index, current_time, screen_point ) )
			return index->get();
	}
	return 0;
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
t_combat_object_const_iterator t_battlefield::object_hit_test( t_uint32				current_time,
															   t_screen_point const &	screen_point ) const
{
	t_object_const_iterator end = m_object_ptr_list.end();
	t_object_const_iterator result = m_object_ptr_list.begin();

	for ( ; result != end; ++result )
	{
		if ( object_hit_test( **result, current_time, screen_point ) )
			break;
	}
	return result;
}

// -----------------------------------------------------------------------
// check if it's legal to place an object in a location
// point is in cell coordinates
// -----------------------------------------------------------------------

bool t_battlefield::off_map( int footprint_size, t_map_point_2d const& point ) const
{
	t_map_point_2d				offset;
	t_map_point_2d				new_point;
	t_combat_footprint const&	footprint = get_combat_footprint( footprint_size );
	int const*					ptr;
	
	for (offset.row = 0; offset.row < footprint_size; offset.row++)
	{
		offset.column = 0;
		new_point = point + offset;
		ptr = footprint[offset.row];
		while (!(*ptr))
		{
			++ptr;
			++new_point.column;
		}
		if (!m_cell_map.is_valid( new_point ))
			return true;
		offset.column = footprint_size - 1;
		new_point = point + offset;
		ptr = footprint[offset.row] + footprint_size - 1;
		while (!(*ptr))
		{
			--ptr;
			--new_point.column;
		}
		if (!m_cell_map.is_valid( new_point ))
			return true;
	}
	return false;
}

// -----------------------------------------------------------------------
// check if it's legal to place an object in a location
// point is in cell coordinates
// -----------------------------------------------------------------------

bool t_battlefield::off_map( t_combat_object_base const& object, t_map_point_2d const& point ) const
{
	t_combat_footprint const& footprint = object.get_footprint();
	int                       footprint_size = footprint.get_size();

	return off_map( footprint_size, point );
}

// -----------------------------------------------------------------------
// check if it's legal to place an object in a location
// point is in cell coordinates
// -----------------------------------------------------------------------

bool t_battlefield::can_place( t_combat_object_base const&		object, 
							   t_map_point_2d const&			point,
							   t_abstract_combat_object const*  ignored_object ) const
{
	t_combat_footprint const& footprint = object.get_footprint();
	int                       footprint_size = footprint.get_size();
	t_map_point_2d            offset;
	t_map_point_2d            new_point;
	int const*				  ptr = footprint.get();
	bool					  is_creature = object.is_creature();
	
	for (offset.row = 0; offset.row < footprint_size; offset.row++)
	{
		for (offset.column = 0; offset.column < footprint_size; offset.column++)
		{
			if (!(*(ptr++)))
				continue;
			new_point = point + offset;
			if (!is_valid( new_point ))
				return false;
			if (m_cell_map.get( new_point ).is_blocked( &object, ignored_object ))
				return false;
		}
	}
	return true;
}

// -----------------------------------------------------------------------
// check if it's legal to place an object in a location
// point is in cell coordinates
// -----------------------------------------------------------------------

bool t_battlefield::can_place( t_combat_object_base const& object, t_map_point_2d const& point ) const
{
	t_combat_footprint const& footprint = object.get_footprint();
	int                       footprint_size = footprint.get_size();
	t_map_point_2d            offset;
	t_map_point_2d            new_point;
	int const*				  ptr = footprint.get();
	bool					  is_creature = object.is_creature();
	
	for (offset.row = 0; offset.row < footprint_size; offset.row++)
	{
		for (offset.column = 0; offset.column < footprint_size; offset.column++)
		{
			if (!(*(ptr++)))
				continue;
			new_point = point + offset;
			if (!is_valid( new_point ))
				return false;
			if (m_cell_map.get( new_point ).is_blocked( &object ))
				return false;
		}
	}
	return true;
}

// -----------------------------------------------------------------------
// check if it's legal to place an object in a location
// point is in cell coordinates
// -----------------------------------------------------------------------
bool t_battlefield::can_place( t_map_point_2d const& point, int footprint_size ) const
{
	t_combat_footprint const& footprint = get_combat_footprint( footprint_size );
	t_map_point_2d            offset;
	t_map_point_2d            new_point;
	int const*				  ptr = footprint.get();
	
	for (offset.row = 0; offset.row < footprint_size; offset.row++)
	{
		offset.column = 0;
		new_point = point + offset;
		if (!m_cell_map.is_valid( new_point ))
			return false;
		offset.column = footprint_size;
		new_point = point + offset;
		if (!m_cell_map.is_valid( new_point ))
			return false;
		for (offset.column = 0; offset.column < footprint_size; offset.column++)
		{
			if (!(*(ptr++)))
				continue;
			new_point = point + offset;
			if (m_cell_map.get( new_point ).is_blocked( 0 ))
				return false;
		}
	}
	new_point = point + offset;
	if (!m_cell_map.is_valid( new_point ))
		return false;
	return true;
}


// -----------------------------------------------------------------------
// set buttons depending on state
// -----------------------------------------------------------------------
void t_battlefield::set_buttons()
{
	if (m_state != k_combat_state_awaiting_human_order)
	{
		m_combat_window->enable_all_buttons( false );
		m_combat_window->show_cancel_spell_button( false );
		
		if (m_state != k_combat_state_finished)
		{
			if ( m_auto_combat_flags[0] )
			{
				m_combat_window->enable_auto_combat(true);
			}
		}
	}
	else
	{
		m_current_cursor_mode = m_acting_creature->get_preferred_action();
		if (m_current_cursor_mode == k_combat_mode_spell 
			&& m_acting_creature->get_spell_points() == 0)
			m_current_cursor_mode = k_combat_mode_ranged;
		if (m_current_cursor_mode == k_combat_mode_ranged && !can_shoot( *m_acting_creature ))
			m_current_cursor_mode = k_combat_mode_melee;
		m_combat_window->set_cursor_mode( m_current_cursor_mode );

		if (m_spell == 0)
		{
			m_combat_window->enable_all_buttons( true );
			m_combat_window->enable_cast_spell( m_acting_creature->can_cast_spells() );
			m_combat_window->enable_wait( !m_acting_creature->is_waiting() );
			m_combat_window->show_cancel_spell_button( false );
		}
		else
		{
			m_combat_window->enable_all_buttons( false );
			m_combat_window->enable_cancel_spell( true );
			m_combat_window->show_cancel_spell_button( true );
		}

		m_combat_window->enable_options( true );
	}
}

// -----------------------------------------------------------------------
// choose a cursor mode
// -----------------------------------------------------------------------
void t_battlefield::set_cursor_mode( t_combat_cursor_mode mode )
{
	m_acting_creature->set_preferred_action( mode );
	m_current_cursor_mode = mode;
	m_combat_window->set_cursor_mode( mode );
}


// -----------------------------------------------------------------------
// choose a cursor mode
// -----------------------------------------------------------------------
static t_external_string const k_text_melee( "melee.combat" );
static t_external_string const k_text_ranged( "ranged.combat" );
static t_external_string const k_text_move( "move.combat" );
static t_external_string const k_text_cast_spell( "cast_spell.combat" );
static t_external_string const k_text_strike_and_return( "strike_and_return.misc" );

void t_battlefield::cursor_mode_menu( t_button* button )
{
	t_scroll_menu*					  menu = new t_scroll_menu( m_combat_window );
	t_handler_1<t_combat_cursor_mode> handler;

	handler = bound_handler( *this, &t_battlefield::set_cursor_mode );

	if (m_acting_creature->get_spell_points() > 0)
		menu->add_item( k_text_cast_spell,  add_argument( handler, k_combat_mode_spell ));
	menu->add_item( k_text_melee, add_argument( handler, k_combat_mode_melee ));
	menu->add_item( k_text_move, add_argument( handler, k_combat_mode_move ));
	if (can_shoot( *m_acting_creature ))
		menu->add_item( k_text_ranged, add_argument( handler, k_combat_mode_ranged ));
	if (m_acting_creature->has_ability( k_ability_strike_and_return ))
		menu->add_item( k_text_strike_and_return, 
		                add_argument( handler, k_combat_mode_strike_and_return ));
	menu->open( m_combat_window->to_client( button->get_screen_rect() ).top_left() );
}


// -----------------------------------------------------------------------
// get icons for messages
// -----------------------------------------------------------------------
static t_bitmap_group_ptr get_message_icon( double scale )
{
	static std::vector<t_bitmap_group_cache> caches;
	static std::vector<double>				 scales;
	int i;

	for (i = 0; i < scales.size(); ++i)
	{
		if (scales[i] == scale)
			break;
	}
	if (i == scales.size())
	{
		caches.push_back( t_bitmap_group_cache( "icons.combat_messages", scale ) );
		scales.push_back( scale );
	}
	return caches[i].get();
}

// -----------------------------------------------------------------------
// add an icon to a message
// -----------------------------------------------------------------------
static void add_message_icon( t_battlefield&	battlefield, 
							  t_message_icon	icon_id, 
							  t_window*			text_window, 
							  t_window*			timed_window )
{
	static char const* k_message_icon_name[] = 
	{ 
		"damage", "death", "heal", "stoned", "angel", "spell_points", "good_luck",
		"bad_luck", "shield"
	};

	t_window*			window;
	t_screen_point		screen_point;
	t_bitmap_group_ptr	bitmap;

	bitmap = get_message_icon( battlefield.get_model_scale() );
	screen_point = text_window->get_screen_rect().top_right();
	screen_point = timed_window->to_client( screen_point );
	window = new t_bitmap_group_window( bitmap, screen_point, 
			                            k_message_icon_name[icon_id], timed_window );
}

// -----------------------------------------------------------------------
// add damage text
// -----------------------------------------------------------------------
void t_battlefield::add_damage_text( t_combat_creature const& creature,
									 int damage, int deaths )
{
	if (!get_show_combat_damage() && (!get_show_combat_deaths() || deaths == 0))
		return;
	
	t_screen_point		screen_point;
	t_map_point_3d		position = creature.get_position();
	t_pixel_24          color = creature.get_message_color();
	t_timed_window*		timed_window;
	t_text_window*		text_window;
	std::string			text;
	t_cached_font		font = get_font( 40 * get_model_scale() );
	t_message_icon		message_icon = k_message_icon_damage;

	if (creature.got_good_luck())
		message_icon = k_message_icon_good_luck;
	if (creature.got_bad_luck())
		message_icon = k_message_icon_bad_luck;

	// compute position of drifting text
	position.height += creature.get_height();
	screen_point = subcell_to_screen_point( position );

	// create drifting parent
	t_screen_point	drift(-1,-1);
	t_screen_point	point(0,0);
	int				drift_time = 3000 * 100 / get_combat_animation_speed();

	timed_window = new t_timed_window( screen_point, m_viewer_ptr, drift_time, drift, 100 );
	if (get_show_combat_damage())
	{
		text = format_string( "-%i", damage );
		text_window = new t_text_window( font, t_screen_point(0,0), timed_window, text, color );
		point = text_window->get_client_rect().bottom_right();
		text_window->set_drop_shadow( true );
		add_message_icon( *this, message_icon, text_window, timed_window );
	}

	if (deaths > 0 && get_show_combat_deaths())
	{
		text = format_string( "-%i", deaths );
		text_window = new t_text_window( font, point, timed_window, text, color );
		text_window->set_drop_shadow( true );
		point.x -= text_window->get_width();
		if (point.x < 0)
			point.x = 0;
		text_window->move( point );
		add_message_icon( *this, k_message_icon_death, text_window, timed_window );
	}
	add_drift_window( timed_window );
}

// -----------------------------------------------------------------------
// add drifiting text
// -----------------------------------------------------------------------
void t_battlefield::add_text( t_abstract_combat_object& creature, std::string const& text,
							  t_pixel_24 const& color, t_message_icon icon_id )
{
	t_screen_point		screen_point;
	t_map_point_3d		position = creature.get_position();
	t_timed_window*		timed_window;
	t_text_window*		text_window;
	t_bitmap_group_ptr	bitmap;

	switch (icon_id)
	{
		case k_message_icon_damage:
		case k_message_icon_shield:
			if (!get_show_combat_damage())
				return;
			break;

		case k_message_icon_death:
			if (!get_show_combat_deaths())
				return;
			break;

		default:
			if (!get_show_other_combat_text())
				return;
			break;
	}

	// compute position of drifting text
	position.height += creature.get_height();
	screen_point = subcell_to_screen_point( position );

	// create drifting parent
	t_screen_point	drift(-1,-1);
	int				drift_time = 3000 * 100 / get_combat_animation_speed();

	timed_window = new t_timed_window( screen_point, m_viewer_ptr, drift_time, drift, 100 );
	text_window = new t_text_window( get_font( 40 * get_model_scale() ), t_screen_point(0,0), 
		                             timed_window, text, color );
	text_window->set_drop_shadow( true );
	if (icon_id != k_message_icon_none)
		add_message_icon( *this, icon_id, text_window, timed_window );
	add_drift_window( timed_window );
}

// -----------------------------------------------------------------------
// add drifiting text
// -----------------------------------------------------------------------
void t_battlefield::add_drift_window( t_timed_window* timed_window )
{
	t_screen_rect					window_rect = timed_window->get_screen_rect();
	t_screen_rect					rect;
	t_timed_window_list::iterator	index = m_drift_windows.begin();
	t_timed_window_list::iterator	end = m_drift_windows.end();
	t_screen_point					change(0,0);
	t_screen_point					drift = timed_window->get_drift();
	t_screen_rect					client_rect;

	client_rect = m_viewer_ptr->to_client( window_rect );
	if (client_rect.top < 0)
		window_rect += t_screen_point( 0, -client_rect.top );
	if (client_rect.left < 0)
		window_rect += t_screen_point( -client_rect.left, 0 );
	while (index != end)
	{
		rect = index->get()->get_screen_rect();
		if (index->get()->get_drift() == drift && intersect( window_rect, rect ))
		{
			change.y = rect.bottom - window_rect.top;
			window_rect += change;
			index = m_drift_windows.begin();
		}
		else
		{
			++index;
		}
	}

	timed_window->move_screen_rect( window_rect );
	client_rect = m_viewer_ptr->to_client( window_rect );

	t_screen_point total_drift = timed_window->get_total_drift();

	if (total_drift.y + client_rect.top < 0)
		drift.y = 0;
	if (total_drift.x + client_rect.left < 0)
		drift.x = 0;
	timed_window->set_drift( drift );

	t_window_handler handler;

	handler = bound_handler( *this, &t_battlefield::remove_drift_window );
	timed_window->set_end_handler( handler );
	m_drift_windows.push_back( timed_window );
}

// -----------------------------------------------------------------------
// add a creature label
// -----------------------------------------------------------------------
void t_battlefield::add_creature_label( t_abstract_combat_object* label )
{
	m_creature_label_list.push_back( label );
}

void t_battlefield::remove_creature_label( t_abstract_combat_object* label )
{
	t_combat_object_ptr object = label;

	m_creature_label_list.remove( object );
}

t_map_point_3d t_battlefield::compute_label_position( t_abstract_combat_object const* label,
													  t_map_point_3d const&			  position ) const
{
	t_screen_rect						label_rect = label->get_rect();
	int									start_y;
	t_screen_rect						rect;
	t_object_ptr_list::const_iterator	index = m_creature_label_list.begin();
	t_object_ptr_list::const_iterator	end	= m_creature_label_list.end();
	t_abstract_combat_object const*		object;
	t_screen_point						change(0,0);

	label_rect += subcell_to_screen_point( position );
	start_y = label_rect.top;
	if (label_rect.top < 0)
		label_rect -= t_screen_point( 0, label_rect.top );
	while ( index != end)
	{
		object = *index;
		if (object == label)
		{
			++index;
			continue;
		}
		rect = object->get_rect() + object->get_screen_position();
		if ( intersect( label_rect, rect ))
		{
			change.y = rect.bottom - label_rect.top + 1;
			label_rect += change;
			index = m_creature_label_list.begin();
		}
		else
		{
			++index;
		}
	}

	int height_change = start_y - label_rect.top;

	height_change = as<int>(height_change / get_view_ratio().y);

	t_map_point_3d result = position;

	result.height += height_change;
	return result;
}

// -----------------------------------------------------------------------
// remove drifiting text
// -----------------------------------------------------------------------
void t_battlefield::remove_drift_window( t_window* window )
{
	t_timed_window_list::iterator index = m_drift_windows.begin();
	t_timed_window_list::iterator end = m_drift_windows.end();

	while (index != end)
	{
		if ( index->get() == window )
		{
			m_drift_windows.erase( index );
			return;
		}
		++index;
	}
}

// -----------------------------------------------------------------------
// add a prompt
// -----------------------------------------------------------------------
void t_battlefield::add_prompt( t_combat_creature& creature, std::string const& text )
{
	t_screen_point screen_point;
	t_map_point_3d position = creature.get_position();
	t_window*	   timed_window;
	t_text_window* text_window;

	// compute position of drifting text
	position.height += creature.get_height();
	screen_point = subcell_to_screen_point( position );

	// create drifting parent
	timed_window = new t_timed_window( screen_point, m_viewer_ptr, 3000, 
                                       t_screen_point( 0, 0 ), 100 );
	text_window = new t_text_window( get_font( 30 * get_model_scale() ), 
		                             t_screen_point(0,0), 
		                             timed_window, text, t_pixel_24(0,255,0) );
	text_window->set_drop_shadow( true );
	timed_window->fit_to_parent_rect();
}

static t_external_string const k_combat_action_regenerate_ability( "combat_action.regenerate_ability" );

// -----------------------------------------------------------------------
// check for regeneration
// -----------------------------------------------------------------------
bool t_battlefield::check_regeneration( t_combat_creature& creature )
{
	if (creature.has_regenerated())
		return false;
	if (!creature.has_ability( k_ability_regeneration )
		&& !creature.is_active( k_spell_regeneration ))
		return false;
	if (creature.get_wounds() == 0)
		return false;

	t_combat_action_message message;

	message.set_creature( creature );

	int healed;

	if (creature.has_ability( k_ability_regeneration ))
	{
		t_hero* hero;

		hero = creature.get_hero();
		if (hero == 0)
			healed = 50;
		else
			healed = hero->get_level() * 2 + 20;

		message.set_text( k_combat_action_regenerate_ability );
	}
	else
	{
		healed = creature.get_effect_power( k_spell_regeneration );
		message.set_text( get_short_spell_name( k_spell_regeneration ) );
	}
	start_pre_action_animation( creature, k_spell_regeneration, message );

	if (healed > creature.get_wounds())
		healed = creature.get_wounds();
	creature.set_regenerated( true );
	creature.heal( healed );
	add_text( creature, format_string( "+%i", healed ), creature.get_message_color(),
		      k_message_icon_heal );
	return true;
}

void t_battlefield::animation_started( t_counted_animation* animation )
{
	m_running_animations++;
	m_running_animation_list.push_back( animation );
}

void t_battlefield::animation_ended( t_counted_animation* animation )
{
	--m_running_animations;
	m_running_animation_list.remove( animation );
}

bool t_battlefield::set_acting( t_combat_creature* actor )
{
	assert( actor );

	t_combat_label* label;

	// Inform data cache that previous action is complete, and 
	// we must throw away most cached information
	m_data_cache.new_action();
	assert( m_state == k_combat_state_selecting_creature );

	if (m_acting_creature != 0)
	{
		label = m_acting_creature->get_label();
		select( *m_acting_creature, false );
		m_acting_creature->set_active_shadow( false );
		invalidate( *label );
		label->set_selected( false );
		invalidate( *label );
	}
	m_acting_creature = actor;
	m_spell = 0;
	m_distance_moved = 0;
	remove_movement_shading();

	t_spell spell = actor->get_default_spell();

	if (spell == k_spell_none)
		m_default_spell = 0;
	else
		m_default_spell = create_combat_spell( *this, spell);

	m_sequence_loader.clear();
	m_acting_creature->set_defending( false );

	// early exit if we're still running an animation
	if (m_running_animations > 0)
		return true;
	// display good morale, if anyone got it.
	if (show_good_morale())
	{	// if they did, and we didn't, we need to switch creatures.
		return m_acting_creature->got_good_morale();
	}
	// show bad morale, if anyone got it.
	if (show_bad_morale())
	{
		// if we got bad morale, we need to switch creatures.
		return !m_acting_creature->got_bad_morale();
	}
	if (check_regeneration( *actor ))
		return true;
	if (actor->is_active( k_spell_poison ) && !actor->get_poison_checked())
	{
		t_combat_action_message message( *actor, get_short_spell_name( k_spell_poison ) );

		actor->set_poison_checked( true );
		start_pre_action_animation( *actor, k_spell_poison, message );
		actor->add_damage( actor->get_effect_power( k_spell_poison ), 0, false, false, message );
		if (actor->get_new_damage() < actor->get_total_hits())
			actor->pre_action_flinch( message );
		actor->apply_damage( *this, true );
		if (actor->get_number() == 0)
		{
			m_state = k_combat_state_action_begun;
		}
		return true;
	}
	if (actor->is_active( k_spell_plague ) && !actor->get_plague_checked())
	{
		t_combat_action_message message( *actor, get_short_spell_name( k_spell_plague ) );

		actor->set_plague_checked( true );
		start_pre_action_animation( *actor, k_spell_plague, message );
		actor->add_damage( actor->get_effect_power( k_spell_plague ), 0, false, false, message );
		if (actor->get_new_damage() < actor->get_total_hits())
			actor->pre_action_flinch( message );
		actor->apply_damage( *this, true );
		if (actor->get_number() == 0)
			m_state = k_combat_state_action_begun;
		return true;
	}

	static t_spell k_incapcitating_spells[] = 
	{
		k_spell_blind, k_spell_stun, k_spell_terror, k_spell_confusion, k_spell_summon_stinkbug
	};

	int i;

	for (i = 0; i < ELEMENTS_OF( k_incapcitating_spells ); ++i)
	{
		if (actor->is_active( k_incapcitating_spells[i] ))
		{
			begin_action( false );
			t_combat_action_message message( *actor, get_short_spell_name( k_incapcitating_spells[i] ) );
			start_animation( *actor, k_incapcitating_spells[i], message );
			return true;
		}
	}
	// doesn't cover freeze, which is inappropriate to play above, so...
	if (actor->is_incapacitated())
	{
		actor->set_moved( true );
		actor->decrement_durations();
		return false;
	}
	
	if (actor->has_ability( k_ability_mana_leech ) && begin_mana_leech( *this ))
		return true;

	// Dive right in
	m_state = k_combat_state_choosing_action;

	m_current_cursor_mode = actor->get_preferred_action();
	actor->set_melee_action( t_ai_melee_action_ptr() );
	m_last_was_defender = actor->get_controller();
	label = actor->get_label();
	invalidate( *label );
	label->set_selected( true );
	invalidate( *label );
	select( *actor, true );
	actor->set_active_shadow( true );
	m_combat_window->display_creature_ready( actor );
	m_current_user_action = 0;
	update_retaliation();
	
	// self reference, since choose action can delete this.
	t_counted_ptr<t_battlefield> ref = this;

	choose_action();
	// must come after Choose Action because state isn't set before then.
	if (m_viewer_ptr != 0)
		m_viewer_ptr->update_cursor();

	// reset the positions of creature labels. Start from scratch, then
	// check for relative positions.
	t_combat_creature_list::reverse_iterator creature_index;
	
	for (creature_index = m_creature_list.rbegin(); creature_index != m_creature_list.rend();
	     ++creature_index)
		(*creature_index)->refresh_label_position( false );
	for (creature_index = m_creature_list.rbegin(); creature_index != m_creature_list.rend();
	     ++creature_index)
		(*creature_index)->refresh_label_position( true );
	return true;
}


// -----------------------------------------------------------------------
// determine direction from one actor to another
// -----------------------------------------------------------------------
t_direction get_direction( t_combat_object_base const& left, 
						   t_map_point_2d                  left_position,
						   t_combat_object_base const& right,
						   t_map_point_2d                  right_position )
{
	int footprint_size	= left.get_footprint_size();
	int offset			= footprint_size << (k_battlefield_subcells_per_cell - 1);

	left_position <<= k_battlefield_subcells_per_cell;
	left_position.row += offset;
	left_position.column += offset;

	footprint_size = right.get_footprint_size();
	offset = footprint_size << (k_battlefield_subcells_per_cell - 1);
	right_position <<= k_battlefield_subcells_per_cell;
	right_position.row += offset;
	right_position.column += offset;

	return get_direction( left_position, right_position );
}


// -----------------------------------------------------------------------
// compute ranged damage
// -----------------------------------------------------------------------
int t_battlefield::get_ranged_damage( t_combat_creature const& attacker,
									  t_combat_creature const& defender ) const
{
	int damage = attacker.get_damage( defender, true, 0, k_wall_bonus_none );
	int factor = get_range_factor( attacker, defender );

	if (factor > 1)
		damage /= factor;
	if (damage < 1)
		damage = 1;
	return damage;
}

static t_external_string const k_combat_action_ranged_attack( "combat_action.ranged_attack" );
static t_external_string const k_combat_action_ranged_retaliation( "combat_action.ranged_retaliation" );

// -----------------------------------------------------------------------
// begin a ranged attack
// -----------------------------------------------------------------------
void t_battlefield::begin_ranged_attack( t_combat_creature&	   attacker, 
										 t_map_point_3d const& target_center,
										 bool				   is_retaliation )
{
	t_play_combat_animation_ptr	   animation;
	t_direction                    direction;
	t_combat_action_message		   message( attacker, 
											is_retaliation ? k_combat_action_ranged_retaliation : k_combat_action_ranged_attack, 
											k_combat_action_message_priority_physical_attack );

	direction = get_direction( attacker.get_footprint_center(), target_center );
	m_sequence_loader.add( attacker.get_cache( k_combat_actor_action_ranged, direction ) );
	animation = new t_play_combat_animation( attacker, direction,
		                                     k_combat_actor_action_ranged );

	animation->display_action_message( message );

	t_combat_creature_handler handler;

	handler = add_2nd_argument( add_3rd_argument( bound_handler( *this, &t_battlefield::create_missile), 
																  is_retaliation), target_center );
	animation->set_key_frame_handler( handler );
	animation->set_end_handler( m_end_animation_handler );
	attacker.set_animation( animation );
	attacker.expend_shot();
	m_data_cache.clear_paths();
}

// -----------------------------------------------------------------------
// begin a ranged attack
// -----------------------------------------------------------------------
void t_battlefield::begin_ranged_attack( t_combat_creature&		   attacker, 
										 t_combat_creature&		   defender,
										 bool					   is_retaliation )
{
	t_map_point_3d target_center = defender.get_footprint_center();

	t_combat_action_message	message( attacker, 
									 is_retaliation ? k_combat_action_ranged_retaliation : k_combat_action_ranged_attack, 
									 k_combat_action_message_priority_physical_attack );

	target_center.height += defender.get_height() / 2;
	begin_ranged_attack( attacker, target_center, is_retaliation );
	defender.check_blocking();
	add_attack( &attacker, &defender, true, message );
}

// -----------------------------------------------------------------------
// preload a flinch
// -----------------------------------------------------------------------
void t_battlefield::preload_flinch( t_combat_creature& creature, t_direction direction )
{
	t_combat_actor_action_id action;

	direction = creature.get_current_direction();
	if (creature.get_new_damage() >= creature.get_total_hits())
		action = k_combat_actor_action_die;
	else
		action = k_combat_actor_action_flinch;
	m_sequence_loader.add( creature.get_cache( action, direction ));
}

// -----------------------------------------------------------------------
// preload a flinch
// -----------------------------------------------------------------------
void t_battlefield::preload_flinch( t_combat_creature& creature )
{
	preload_flinch( creature, creature.get_current_direction() );
}

// -----------------------------------------------------------------------
// do a ranged retaliation
// -----------------------------------------------------------------------
void t_battlefield::check_ranged_retaliation( t_combat_creature* attacker,
										      t_combat_creature* defender )
{
	t_direction        direction;
	t_combat_creature* retaliation_target = 0;

	// do ranged retailation
	if (attacker->has_retaliated() || !can_shoot( *attacker ) || !attacker->is_awake())
		return;

	retaliation_target = get_ranged_target( *attacker, *defender );
	assert( retaliation_target != 0 );
	if (retaliation_target == 0)
		return;
	attacker->set_retaliated( !attacker->has_ability( k_ability_unlimited_retaliation ) );

	if ( attacker->has_ability( k_ability_area_effect ) )
		begin_area_effect_attack( *attacker, retaliation_target->get_body_center(), true );
	else
		begin_ranged_attack( *attacker, *retaliation_target, true );
	if (defender != retaliation_target)
		preload_flinch( *retaliation_target );
	else
	{
		direction = get_direction( *defender, *attacker );
		preload_flinch( *defender, direction );
	}
	direction = get_direction( *attacker, *retaliation_target );
	preload_flinch( *attacker, direction );
}

// -----------------------------------------------------------------------
// begin an action
// -----------------------------------------------------------------------
void t_battlefield::begin_action( bool clears_sanctuary )
{
	t_creature_list::iterator index = m_selected_creatures.begin();

	m_state = k_combat_state_action_begun;
	if (clears_sanctuary)
		m_acting_creature->clear_spell( k_spell_sanctuary, t_combat_action_message() );
	set_buttons();

	for (; index != m_selected_creatures.end(); index++)
	{
		index->get()->set_selection_shadow( false );
		index->get()->set_active_shadow( false );
		index->get()->set_selected( false );
	}
	m_selected_creatures.clear();
	set_transparency();
}

// -----------------------------------------------------------------------
// begin a ranged attack
// -----------------------------------------------------------------------

void t_battlefield::begin_area_effect_attack( t_combat_creature&    attacker, 
											  t_map_point_3d const& target_center,
											  bool					is_retaliation)
{
	t_creature_list           targets;
	t_creature_list::iterator index;

	t_combat_action_message	message( attacker, 
									 is_retaliation ? k_combat_action_ranged_retaliation : k_combat_action_ranged_attack, 
									 k_combat_action_message_priority_physical_attack );

	targets = get_area_targets( target_center, k_ranged_area_attack_radius );
	index = targets.begin();

	for (; index != targets.end(); index++)
	{
		preload_flinch( **index );
		index->get()->check_blocking();
		add_attack( &attacker, index->get(), true, message );
	}
	begin_ranged_attack( attacker, target_center, is_retaliation );
}

// -----------------------------------------------------------------------
// begin a ranged attack
// -----------------------------------------------------------------------

void t_battlefield::begin_ranged_attack( t_map_point_2d const& target )
{
	t_combat_creature& attacker = *m_acting_creature;
	int                attacks = attacker.get_attacks( true );
	t_map_point_3d     target_center( target, 0 );

	m_target = 0;
	begin_action();
	add_action( add_argument( bound_handler( *this, &t_battlefield::end_ranged_attack ), 
		                      attacks - 1 ));
	target_center <<= k_battlefield_subcell_shift;
	target_center += k_battlefield_half_cell;
	target_center.height = compute_terrain_height( target_center );
	m_target_subcell = target_center;
	begin_area_effect_attack( attacker, target_center, false );
}

// -----------------------------------------------------------------------
// begin a ranged attack
// -----------------------------------------------------------------------
void t_battlefield::begin_ranged_attack( t_combat_creature* defender )
{
	t_combat_creature&	attacker = *m_acting_creature;
	int                 attacks = attacker.get_attacks( true );

	begin_action();
	m_target = defender;
	if (defender->has_retaliated() || !can_shoot( *defender ))
	{
		add_action( add_argument( bound_handler( *this, &t_battlefield::end_ranged_attack ),
			                      attacks - 1 ));
		begin_ranged_attack( attacker, *defender, false );
		preload_flinch( *defender );
		return;
	}

	// do retaliation

	t_combat_creature*  retaliation_target;
	bool				attacker_has_first_strike;
	bool				defender_has_first_strike;
	
	attacker_has_first_strike = attacker.has_ability( k_ability_ranged_first_strike );
	defender_has_first_strike = defender->has_ability( k_ability_ranged_first_strike );
	if (!defender->is_awake())
	{
		attacker_has_first_strike = true;
		defender_has_first_strike = false;
	}

	retaliation_target = get_ranged_target( *defender, attacker );
	assert( retaliation_target != 0 );
	if (!attacker_has_first_strike || defender_has_first_strike)
	{
		add_action( add_argument( bound_handler( *this, 
		                          &t_battlefield::end_ranged_attack ), attacks - 1 ));
		begin_ranged_attack( attacker, *defender, false );
		check_ranged_retaliation( defender, m_acting_creature );
		return;
	}

	add_action( bound_handler( *this, &t_battlefield::begin_ranged_retaliation ) );
	begin_ranged_attack( attacker, *defender, false );
	preload_flinch( *defender );
}

// -----------------------------------------------------------------------
// end a ranged attack
// -----------------------------------------------------------------------
void t_battlefield::end_ranged_attack( int attacks_left )
{
	if (attacks_left <= 0 || m_acting_creature->get_number() <= 0)
	{
		m_acting_creature->clear_spell( k_spell_berserk, t_combat_action_message() );
		return;
	}

	t_handler action;

	action = add_argument( bound_handler( *this, &t_battlefield::end_ranged_attack ), 
			                              attacks_left - 1 );
	if (m_target == 0)
	{
		t_creature_list targets = get_area_targets( m_target_subcell, k_ranged_area_attack_radius );

		if (targets.empty())
		{
			m_acting_creature->clear_spell( k_spell_berserk, t_combat_action_message() );
			return;
		}
		assert( m_acting_creature->has_ability( k_ability_area_effect ) );
		add_action( action );
		begin_area_effect_attack( *m_acting_creature, m_target_subcell, false );
		return;
	}

	t_combat_creature* defender = dynamic_cast<t_combat_creature*>( m_target.get() );

	if (defender->get_number() <= 0)
	{
		m_acting_creature->clear_spell( k_spell_berserk, t_combat_action_message() );
		return;
	}
	add_action( action );
	begin_ranged_attack( *m_acting_creature, *defender, false );
}

// -----------------------------------------------------------------------
// do a retaliation
// -----------------------------------------------------------------------
void t_battlefield::begin_ranged_retaliation()
{
	t_combat_creature& attacker = *m_acting_creature;
	t_combat_creature& defender = dynamic_cast<t_combat_creature&>(*m_target);
	int                attacks  = attacker.get_attacks( true ) - 1;

	if (attacker.get_number() > 0 && defender.get_number() > 0)
	{
		if (!defender.is_awake())
		{
			end_ranged_attack( attacks );
			return;
		}
		check_ranged_retaliation( &defender, &attacker );
		add_action( add_argument( bound_handler( *this, &t_battlefield::end_ranged_attack ), 
			                      attacks ));
	}
}


// -----------------------------------------------------------------------
// launch a ranged missile
// -----------------------------------------------------------------------
void t_battlefield::create_missile( t_combat_creature& actor, t_map_point_3d target_center, bool is_retaliation )
{
	t_missile_handler handler;
	t_map_point_3d    source_position;

	t_combat_action_message message( actor, 
									 is_retaliation ? k_combat_action_ranged_retaliation : k_combat_action_ranged_attack, 
									 k_combat_action_message_priority_physical_attack );

	t_missile_strike_data strike_data( true, message );

	handler = add_3rd_argument( bound_handler( *this, &t_battlefield::missile_strike ), strike_data );
	source_position = actor.get_missile_offset();
	launch_missile( actor.get_missile_type(), actor, source_position, target_center,
	                handler, message );
}

// -----------------------------------------------------------------------
// launch a ranged missile
// -----------------------------------------------------------------------
void t_battlefield::launch_missile( t_missile_type					missile_type,
									t_combat_creature&				source,
									t_map_point_3d const&			source_position,
									t_map_point_3d const&			end_position, 
									t_missile_handler				handler,
									t_combat_action_message const &	message)
{
	t_combat_actor_model_ptr	  missile_model;
	t_combat_actor*				  missile = 0;

	if (missile_type != k_missile_none)
		missile_model = get_combat_actor_model( missile_type, get_model_scale() );
	if (missile_model != 0)
		missile = new t_unsaved_combat_actor( this, missile_model, k_combat_actor_action_walk,
											  k_direction_northeast );
	if (missile == 0)
	{
		handler( &source, end_position );
		return;
	} 

	t_move_missile_ptr				animation;
	t_map_point_3d					position = source_position;
	int								offset = missile->get_half_footprint_size();

	position.row -= offset;
	position.column -= offset;
	place_object( missile, position );
	animation = new t_move_missile( *missile, source, end_position,
		                            is_parabolic( missile_type ), handler );
	animation->display_action_message( message );
	missile->set_animation( animation );
}

namespace
{
	class t_delayed_missile_impact : public t_handler_base_1<t_combat_creature&>
	{
	public:
		t_delayed_missile_impact( t_battlefield&					battlefield,
								  t_direction						direction,
								  t_combat_creature_ptr				attacker,
								  bool								physical_attack,
								  t_combat_action_message const &	message);

		virtual void operator() ( t_combat_creature& defender );
	private:
		t_battlefield&			m_battlefield;
		t_direction				m_direction;
		t_combat_creature_ptr	m_attacker;
		bool					m_physical_attack;
		t_combat_action_message m_message;
	};

	inline t_delayed_missile_impact::t_delayed_missile_impact( t_battlefield&					battlefield,
															   t_direction						direction,
															   t_combat_creature_ptr			attacker,
															   bool								physical_attack,
															   t_combat_action_message const &	message )
								   : m_battlefield( battlefield ), m_direction( direction ),
									 m_attacker( attacker ), m_physical_attack( physical_attack ),
									 m_message( message )

	{
	}

	void t_delayed_missile_impact::operator ()( t_combat_creature& defender )
	{
		m_battlefield.missile_impact( defender, m_direction, m_attacker, m_physical_attack, m_message );
	}
};

static t_external_string const k_combat_action_block( "combat_action.block" );

// -----------------------------------------------------------------------
// handle impact of a missile
// -----------------------------------------------------------------------
void t_battlefield::missile_impact( t_combat_creature&		defender, 
								    t_direction				defend_direction,
									t_combat_creature_ptr	attacker,
									bool					physical_attack,
									t_combat_action_message message )
{
	if (defender.is_busy())
	{
		// set up an an call back that will call this function again when the animation finishes
		t_combat_creature_handler new_action;

		new_action = new t_delayed_missile_impact( *this, defend_direction, attacker, physical_attack, message );
		defender.append_handler( new_action );
		return;
	}
	
	if (defender.get_new_damage() >= defender.get_total_hits())
	{
		set_current_direction( defender, defend_direction );
	}
	else
	{
		t_direction			     direction = defender.get_current_direction();
		t_combat_actor_action_id action = k_combat_actor_action_flinch;
		bool play_animation = true;

		if ( defender.check_guardian_robe() )
			play_animation = false;

		if (defender.is_blocking_attack())
		{
			message.clear();
			message.set_creature( defender );
			message.set_text( k_combat_action_block );
			message.set_priority( k_combat_action_message_priority_block );

			direction = defend_direction;
			if (defender.has_action( k_combat_actor_action_block, direction ))
			{
				start_animation( defender, k_spell_effect_block, message );
				action = k_combat_actor_action_block;
				play_animation = true;
			}
		}

		if ( play_animation )
			defender.set_animation( action, direction, m_end_animation_handler, message );

	}

	if (physical_attack)
	{
		t_attack attack( *this, attacker, &defender, true, defender.get_new_damage() );

		attack.resolve();
	}
	else
	{
		defender.apply_damage( *this, true );
	}
	update_state();
}

// -----------------------------------------------------------------------
// handle impact of a missile
// -----------------------------------------------------------------------
void t_battlefield::missile_strike( t_combat_creature_ptr attacker, t_map_point_2d center,
									t_missile_strike_data data )
{
	t_direction    direction;
	t_map_point_2d cell = center >> k_battlefield_subcell_shift;

	if ( data.get_is_physical_attack() && attacker->has_ability( k_ability_area_effect ))
	{
		t_creature_list           targets = get_area_targets( center, k_ranged_area_attack_radius );
		t_creature_list::iterator index;

		for (index = targets.begin(); index != targets.end(); index++)
		{
			direction = get_direction( index->get()->get_footprint_center(),
				                       center );
			missile_impact( **index, direction, attacker, data.get_is_physical_attack(), data.get_message() );
		}
	}
	else
	{
		t_combat_creature* target = get_creature( cell );
	
		if (target != 0)
			missile_impact( *target, get_direction( *target, *attacker ), attacker, data.get_is_physical_attack(), data.get_message() );
	}
	update_state();
}


// -----------------------------------------------------------------------
// handle end of walk action
// -----------------------------------------------------------------------
void t_battlefield::update_state()
{
	// add self reference because set_acting can close the battlefield on surrender.
	t_battlefield_ptr ref = this;

	switch (m_state)
	{
		case k_combat_state_awaiting_ai_order:
		case k_combat_state_awaiting_human_order:
			return;

		case k_combat_state_finished:
			return;

		case k_combat_state_selecting_creature:
			while (m_running_animations == 0 && !m_pending_actions.empty())
			{
				t_handler handler = m_pending_actions.front();

				m_pending_actions.pop_front();
				handler();
			}
			if (m_running_animations <= 0)
			{
				if (m_acting_creature == 0 || m_acting_creature->get_number() <= 0 
					|| !set_acting( m_acting_creature ))
				{
					select_next_creature();
				}
			}
			break;

		case k_combat_state_action_begun:
			while (m_running_animations == 0 && !m_pending_actions.empty())
			{
				t_handler handler = m_pending_actions.front();

				m_pending_actions.pop_front();
				handler();
			}
			if (m_running_animations <= 0)
			{
				m_acting_creature->set_moved( true );
				m_acting_creature->decrement_durations();
				m_spell = 0;
				m_state = k_combat_state_selecting_creature;
				select_next_creature();
			}
			break;
	}
}

// -----------------------------------------------------------------------
// handle end of a morale animation
// -----------------------------------------------------------------------
void t_battlefield::end_pre_action_animation( t_window* window, t_combat_creature& creature )
{
	update_state();
}


// -----------------------------------------------------------------------
// handle end of a spell animation
// -----------------------------------------------------------------------
void t_battlefield::end_spell( t_window* window, t_combat_creature& creature )
{
	update_state();
}

// -----------------------------------------------------------------------
// handle end of a spell animation
// -----------------------------------------------------------------------
void t_battlefield::end_damage_spell( t_window* window, 
									  t_end_damage_spell_data data )
{
	if ( data.get_spell() != k_spell_lightning)
		data.get_creature()->check_flinch( data.get_message() );
	data.get_creature()->apply_damage( *this, true );
	update_state();
}

// -----------------------------------------------------------------------
// start a spell which will inflict damage when it ends
// -----------------------------------------------------------------------
void t_battlefield::start_damage_spell( t_combat_creature_ptr			caster,
									    t_combat_creature&				creature, 
										int								damage,
                                        t_spell							spell,
										t_combat_action_message const & message)
{
	t_window_handler   end_handler;

	t_end_damage_spell_data data( &creature, spell, message );

	creature.add_damage( damage, caster, true, (spell == k_spell_disintegrate), message );
	end_handler = add_2nd_argument( bound_handler( *this,  &t_battlefield::end_damage_spell ), data );
	get_combat_sound( spell )->play( get_sound_volume() );
	start_animation( creature, spell, end_handler, message );
	if (spell == k_spell_lightning)
	{
		t_combat_actor_action_id action;

		if (creature.get_new_damage() >= creature.get_total_hits())
			action = k_combat_actor_action_die;
		else
			action = k_combat_actor_action_flinch;

		creature.set_animation( action, m_end_animation_handler, message );
	}
	m_spell = 0;
}

// -----------------------------------------------------------------------
// handle end of a flinch or die animation
// -----------------------------------------------------------------------
void t_battlefield::end_animation( t_combat_creature& creature )
{
	update_state();
}

// -----------------------------------------------------------------------
// start a creature walking toward a target
// -----------------------------------------------------------------------
void t_battlefield::move_to_attack( t_abstract_combat_object* target,
								    t_map_point_2d const& point )
{
	t_combat_path_data const*		  path_point;
	t_combat_creature_handler		  handler;

	begin_action();
	m_target = target;
	m_starting_position = m_acting_creature->get_cell_position();
	path_point = &get_current_path_finder().get_data( point );
	if (path_point->move_cost > 0)
	{
		t_direction    direction;

		if (path_point->move_cost > m_acting_creature->get_combat_movement())
		{
			begin_move( point );
			return;
		}
		handler = bound_handler( *this, &t_battlefield::begin_attack );
		begin_move( point, handler );
		direction = get_direction( *m_acting_creature, point, *m_target );
		m_sequence_loader.add( m_acting_creature->get_cache(
			                   k_combat_actor_action_melee, direction ));
		return;
	}
	begin_attack( *m_acting_creature );
}

// -----------------------------------------------------------------------
// check if the gate is inside a footprint
// -----------------------------------------------------------------------
static bool touches_gate( t_battlefield& battlefield, t_map_point_2d const& point, 
						  t_combat_footprint const& footprint )
{
	int									footprint_size = footprint.get_size();
	t_map_point_2d						offset;
	t_map_point_2d						new_point;
	int const*							ptr = footprint.get();
	t_battlefield_cell const*			cell;
	t_stationary_combat_object const*	obstacle;
	
	for (offset.row = 0; offset.row < footprint_size; offset.row++)
	{
		for (offset.column = 0; offset.column < footprint_size; offset.column++)
		{
			if (!(*(ptr++)))
				continue;
			new_point = point + offset;
			cell = &battlefield.get_cell( new_point );
			obstacle = cell->get_obstacle();
			if (obstacle != 0 && obstacle->get_obstacle_type() == k_obstacle_castle_gate)
				return true;
		}
	}
	return false;
}

// -----------------------------------------------------------------------
// check if a move crosses the castle gate
// -----------------------------------------------------------------------
static bool crosses_gate( t_battlefield& battlefield, t_combat_path& path, 
						  t_combat_footprint const& footprint )
{
	int i;

	for (i = 0; i < path.size(); i++)
		if (touches_gate( battlefield, path[i], footprint ))
			return true;
	return false;
}

// -----------------------------------------------------------------------
// move to a point
// -----------------------------------------------------------------------
bool t_battlefield::begin_move( t_combat_creature&		  creature,
							    t_combat_path&			  path,
							    t_map_point_2d const&	  point, 
							    t_combat_creature_handler handler,
								t_combat_action_message const & message )
{
	int                               i;

	if (creature.has_ability( k_ability_binding ))
		creature.clear_bindings();
	if (!gate_is_open() && touches_gate( *this, point, creature.get_footprint_size() ))
	{
		t_open_gate_animation_ptr open_gate_animation;
		open_gate_animation = new t_open_gate_animation( creature, path, point, handler );
		open_gate_animation->display_action_message( message );
		open_gate( open_gate_animation.get() );
		return true;
	}

	m_data_cache.clear_paths();
	if (creature.has_ability( k_ability_teleport ) || creature.is_flying())
	{
		t_map_point_3d destination( point, 0 );

		destination <<= k_battlefield_subcell_shift;
		destination.height = compute_terrain_height_under_object( creature, 
		                                                          destination );
		// check if target location is in a tower.
		t_stationary_combat_object* tower;

		tower = get_tower( destination, creature.get_footprint() );
		if (tower != 0)
			destination = creature.get_position_in_tower( tower );

		if (creature.is_in_tower())
			creature.remove_from_tower();
		if (creature.has_ability( k_ability_teleport ))
		{
			t_teleport_ability_ptr teleport_animation;
			teleport_animation = new t_teleport_ability( creature, destination, handler );
			teleport_animation->display_action_message( message );
			creature.set_animation( teleport_animation );
			return true;
		}

		t_play_combat_flight_ptr flight_animation;
		flight_animation = new t_play_combat_flight( creature, destination, handler );
		flight_animation->display_action_message( message );
		creature.set_animation( flight_animation );
		return true;
	}

	// preload needed frames.
	m_sequence_loader.add( creature.get_cache( k_combat_actor_action_prewalk,
		                   path.back().direction ) );
	i = path.size() - 1;
	while (i--)
	{
		m_sequence_loader.add( creature.get_cache( k_combat_actor_action_walk,
			                   path[i].direction ));
	}
	m_sequence_loader.add( creature.get_cache( k_combat_actor_action_postwalk,
		                   path.front().direction ));

	if (!gate_is_open() && crosses_gate( *this, path, creature.get_footprint_size() ))
	{
		t_open_gate_animation_ptr open_gate_animation;
		open_gate_animation = new t_open_gate_animation( creature, path, point, handler );
		open_gate_animation->display_action_message( message );
		open_gate( open_gate_animation.get() );
		return true;
	}

	t_play_combat_walk_ptr walk_animation;
	walk_animation = new t_play_combat_walk( creature, path, handler );
	walk_animation->display_action_message( message );
	creature.set_animation( walk_animation );
	return true;
}


// -----------------------------------------------------------------------
// move to a point
// -----------------------------------------------------------------------
bool t_battlefield::begin_move( t_map_point_2d const&     point, 
							    t_combat_creature_handler handler )
{
	t_combat_path path;

	if (!get_current_path_finder().get_path( point, path ))
		return false;
	m_distance_moved = path.front().move_cost;
	begin_move( *m_acting_creature, path, point, handler );
	return true;
}

static int get_next_waypoint_index( t_combat_path const& path, int move_distance )
{
	t_combat_path_point const*  path_point;
	int							i;

	for (i = 0; i < path.size(); i++)
	{
		path_point = &path[i];
		if (path_point->move_cost <= move_distance && !path_point->blocked)
		{
			return i;
		}
	}
	assert( false );
	return 0;
}

// -----------------------------------------------------------------------
// get a waypoint along the way to a point
// -----------------------------------------------------------------------
t_map_point_2d t_battlefield::get_waypoint( t_map_point_2d const& point ) const
{
	t_combat_path				path;
	int							move_distance = m_acting_creature->get_combat_movement();
	int							i;
	
	get_current_path_finder().get_path( point, path );
	i = get_next_waypoint_index( path, move_distance );
	return path[i];
}

// -----------------------------------------------------------------------
// move to a point
// -----------------------------------------------------------------------
bool t_battlefield::begin_move( t_map_point_2d const& point )
{
	t_combat_creature_handler handler;
	t_map_point_2d            waypoint = get_waypoint( point );

	handler = bound_handler( *this, &t_battlefield::end_walk );
	if (!begin_move( waypoint, handler ))
		return false;
	begin_action( false );
	return true;
}


// -----------------------------------------------------------------------
// get creature at cursor position.  Uses footprint first, then creature outline.
// -----------------------------------------------------------------------
t_combat_creature* t_battlefield::creature_hit_test( t_screen_point const& screen_point ) const
{
	t_map_point_2d     point;

	if (cell_hit_test( screen_point, point ))
	{
		t_combat_creature* creature = 0;

		creature = m_cell_map.get( point ).get_creature();
		if (creature != 0)
			return creature;
	}

	t_creature_list::const_reverse_iterator end = m_creature_list.rend();
	t_creature_list::const_reverse_iterator index = m_creature_list.rbegin();
	t_uint32                        current_time = get_time();

	for ( ; index != end; ++index )
	{
		if (index->get()->get_number() == 0)
			continue;
		if ( object_hit_test( **index, current_time, screen_point ) )
			return *index;
	}

	return 0;
}

// -----------------------------------------------------------------------
// handle a right click
// -----------------------------------------------------------------------
void t_battlefield::right_click( t_screen_point const& screen_point )
{
	if (m_state != k_combat_state_awaiting_human_order)
		return;

	t_combat_creature* creature = creature_hit_test( screen_point );

	if (creature == 0)
		return;

	if ( get_show_combat_movement_shading() )
		set_movement_shading( *creature );

	t_screen_point window_origin;
	t_window*      help_window;
	
	window_origin = m_combat_window->to_client( m_viewer_ptr->to_screen( screen_point ) );
	help_window = new t_dialog_combat_creature( creature, this, window_origin, m_combat_window,
		                                        m_combat_window->get_client_rect() );

}

// -----------------------------------------------------------------------
// Called by t_dialog_combat_creature when it closes
// -----------------------------------------------------------------------
void t_battlefield::right_click_dialog_closed()
{
	// Reset movement shading in 3 seconds
	if ( get_show_combat_movement_shading() )
		m_movement_shading_resetter.set( 3000, bound_handler( *this, &t_battlefield::set_movement_shading_to_default ) );
}

// -----------------------------------------------------------------------
// preload an animation in another thread
// ----------------------------------------------------------------------
void t_battlefield::preload( t_combat_actor& creature, t_combat_actor_action_id action,
							 t_direction direction )
{
	m_sequence_loader.add( creature.get_cache( action, direction ));
}

// ----------------------------------------------------------------------
// code for a multi headed attack
// ----------------------------------------------------------------------
t_attackable_object_list 
t_battlefield::get_multi_attack_targets( t_combat_creature const&   attacker,
									     t_attackable_object const& defender,
										 t_attack_angle const&		attack_angle )
{
	int							attack_distance = attacker.get_attack_range() + 2;
	int							footprint_size	= attacker.get_footprint_size() + attack_distance * 2;
	t_map_point_2d				position		= attack_angle.point;
	t_map_point_2d				offset;
	t_map_point_2d				point;
	t_attackable_object*		candidate;
	t_abstract_combat_object*	object;
	t_direction					direction;
	int							arc = attacker.get_arc_attack();
	t_attackable_object_list	targets;

	targets.push_back( const_cast<t_attackable_object*>(&defender) );
	direction = get_direction( attack_angle.attack_angle );

	t_threat_footprint const& footprint	= get_threat_footprint( footprint_size, direction, arc );

	position.row	-= attack_distance;
	position.column -= attack_distance;
	for (offset.row = 0; offset.row < footprint_size; offset.row++)
	{
		for (offset.column = 0; offset.column < footprint_size; offset.column++)
		{
			// check if point is in attack area
			if (!footprint[offset.row][offset.column])
				continue;
			// check if point is a valid square
			point = position + offset;
			if (!is_valid( point ))
				continue;
			// get creature in square
			object = get_cell( point ).get_attackable_object();
			if (object == 0)
				continue;
			candidate = object->get_attackable_object();
			if (!candidate->is_alive())
				continue;
			// check if creature is friendly
			if (attacker.is_friendly( *candidate ))
				continue;

			// check if creature has already been attacked
			if (std::find( targets.begin(), targets.end(), candidate )
				!= targets.end())
				continue;

			targets.push_back( candidate );
		}
	}
	return targets;
}



// ----------------------------------------------------------------------
// code for a multi headed attack
// ----------------------------------------------------------------------
t_combat_creature_handler t_battlefield::multi_attack( t_combat_creature&				attacker,
							                           t_attackable_object&				defender,
													   bool								flinch,
													   t_combat_action_message const &	message )
{
	t_attackable_object_list			targets;
	t_attackable_object_list::iterator	index;
	t_combat_flinch*					combat_flinch;
	t_attackable_object*				candidate;
	t_attack_angle						attack_angle;

	combat_flinch = new t_combat_flinch( attacker, defender, message, !flinch );
	attack_angle.point = attacker.get_cell_position();
	attack_angle.attack_angle = get_angle( attacker.get_footprint_center(), 
										   defender.get_object()->get_footprint_center() );
	targets = get_multi_attack_targets( attacker, defender, attack_angle );

	for (index = targets.begin(); index != targets.end(); ++index)
	{
		candidate = *index;

		// add damage to target
		if (candidate != &defender)
			candidate->check_blocking();
		add_attack( &attacker, candidate, false, message );
		if ( candidate != &defender )
			combat_flinch->add( *candidate );
	}
	return combat_flinch;
}


// -----------------------------------------------------------------------
// check effects of breath attacks
// -----------------------------------------------------------------------

t_combat_creature_list t_battlefield::get_breath_targets( t_combat_creature const&		attacker,
														  t_attackable_object const&	defender,
														  t_map_point_2d const&			cell_point,
														  t_map_point_2d const&			target_point,
														  bool							check_resistance ) const
{
	t_combat_creature_list		result;
	t_map_point_2d				position;
	t_map_point_2d				start;
	t_map_point_2d				delta;
	t_map_point_2d				delta_sign;
	t_map_point_2d				sum(0,0);
	t_map_point_2d				current_delta;
	t_map_point_2d				point;
	t_map_point_2d				cell_position;
	int							threshold;
	int							range;
	int							distance;
	t_attackable_object_list	targets;
	t_combat_creature*			target;

	position = attacker.get_footprint_center( cell_point );
	start = position;
	delta    = defender.get_object()->get_footprint_center( target_point ) - position;
	range = ((attacker.get_attack_range() + k_breath_range) << 1) 
		    + attacker.get_footprint_size();
	range <<= k_battlefield_subcell_shift - 1;
	delta_sign = sign_of( delta );
	delta      = abs( delta );
	threshold  = delta.column;
	if (threshold < delta.row)
		threshold = delta.row;
	delta    <<= k_battlefield_subcell_shift;

	targets.push_back( const_cast<t_combat_creature*>( &attacker ));
	targets.push_back( const_cast<t_attackable_object*>( &defender ));
	assert( threshold > 0 );
	if (threshold == 0)
		return result; // should NEVER happen.
	while (true)
	{
		sum += delta;
		position.column += sum.column * delta_sign.column / threshold;
		position.row    += sum.row * delta_sign.row / threshold;
		sum.column %= threshold;
		sum.row    %= threshold;
		current_delta = position - start;
		distance = sqrt( (double)(current_delta.column * current_delta.column 
			             + current_delta.row * current_delta.row) );
		if (distance > range)
			break;
		cell_position = position >> k_battlefield_subcell_shift;
		if (!is_valid( cell_position ))
			break;
		target = get_creature( cell_position );
		if (target == 0)
			continue;

		// check if creature has already been attacked
		if (std::find( targets.begin(), targets.end(), target ) != targets.end())
			continue;

		// fire based breath attacks do not do collateral damage to fire resistant
		// creatures
		targets.push_back( target );
		if (check_resistance && target->has_ability( k_ability_fire_resistance ))
			continue;
		result.push_back( target );
	}
	return result;
}

static t_external_string const k_combat_action_fire_resistance( "combat_action.fire_resistance" );

// -----------------------------------------------------------------------
// check effects of breath attacks
// -----------------------------------------------------------------------
t_combat_flinch* t_battlefield::check_breath( t_combat_creature&			  attacker,
											  t_attackable_object&			  defender, 
											  t_combat_flinch*				  flinch,
											  t_combat_action_message const & message )
{
	t_combat_creature_list			 targets;
	t_combat_creature_list::iterator index;
	t_combat_creature*				 target;

	targets = get_breath_targets( attacker, defender, attacker.get_cell_position(),
								  defender.get_object()->get_cell_position(), false );

	for (index = targets.begin(); index != targets.end(); ++index)
	{
		// fire based breath attacks do not do collateral damage to fire resistant
		// creatures
		target = *index;
		if (target->has_ability( k_ability_fire_resistance ))
		{
			t_combat_action_message resist_message( *target, k_combat_action_fire_resistance );
			start_animation( *target, k_spell_fire_resistance, resist_message );
			continue;
		}
		target->check_blocking();
		add_attack( &attacker, target, false, message );
		if (flinch == 0)
			flinch = new t_combat_flinch( attacker, *target, message );
		else
			flinch->add( *target );
	}
	return flinch;
}

// -----------------------------------------------------------------------
// show effects of breath attacks
// -----------------------------------------------------------------------
void t_battlefield::show_breath_targets( t_combat_creature&		attacker,
										 t_attackable_object&	defender,
										 t_screen_point const&  screen_point )
{
	t_combat_creature_list			 targets;
	t_combat_creature*				 target;
	t_attack_angle					 closest;

	if (!get_attack_angle( screen_point, &defender, closest ))
	{
		select_target( 0 );
		return;
	}

	targets = get_breath_targets( attacker, defender, closest.point, 
								  defender.get_object()->get_cell_position(), true );
	target = dynamic_cast<t_combat_creature*>( &defender );
	if (target != 0)
		targets.push_back( target );
	select_targets( targets );
}


// -----------------------------------------------------------------------
// show effects of breath attacks
// -----------------------------------------------------------------------
void t_battlefield::show_multi_attack_targets( t_combat_creature&		attacker,
											   t_attackable_object&		defender,
											   t_screen_point const&    screen_point )
{
	t_attackable_object_list			targets;
	t_attackable_object_list::iterator	index;
	t_combat_creature_list				creatures;
	t_combat_creature*					target;
	t_attack_angle						closest;

	if (!get_attack_angle( screen_point, &defender, closest ))
	{
		select_target( 0 );
		return;
	}

	targets = get_multi_attack_targets( attacker, defender, closest );

	for (index = targets.begin(); index != targets.end(); ++index)
	{
		target = dynamic_cast<t_combat_creature*>( *index );
		if (target != 0)
			creatures.push_back( target );
	}
	select_targets( creatures );
}

static t_external_string const k_combat_action_melee_attack( "combat_action.melee_attack" );
static t_external_string const k_combat_action_melee_retaliation( "combat_action.melee_retaliation" );

// -----------------------------------------------------------------------
// handle start of attack.
// -----------------------------------------------------------------------
void t_battlefield::begin_melee_attack( t_combat_creature&   attacker, 
									    t_attackable_object& defender,
										bool				 flinch, 
										bool				 is_retaliation )
{
	t_direction				    direction;
	t_play_combat_animation_ptr animation;
	t_combat_creature_handler   attack_handler;
	t_combat_actor_action_id    action;
	t_combat_action_message     message( attacker, 
								  	     is_retaliation ? k_combat_action_melee_retaliation : k_combat_action_melee_attack,
									     k_combat_action_message_priority_physical_attack ); 

	assert( !attacker.is_friendly( defender ) );
	direction = get_direction( attacker, *defender.get_object() );
	if (crosses_wall( attacker, defender ) != k_wall_bonus_none
		|| defender.is_in_tower())
		action = k_combat_actor_action_melee_up;
	else
		action = k_combat_actor_action_melee;
	m_sequence_loader.add( attacker.get_cache( action, direction ));
	m_data_cache.clear_paths();
	animation = new t_play_combat_animation( attacker, direction, action );
	animation->display_action_message( message );
	attacker.set_animation( animation );

	bool is_3_headed = attacker.has_ability( k_ability_3_headed_attack );
	bool is_hydra    = attacker.has_ability( k_ability_hydra_strike );

	animation->set_end_handler( m_end_animation_handler );
	if (is_3_headed || is_hydra)
	{
		attack_handler = multi_attack( attacker, defender, flinch, message );
	}
	else
	{
		t_combat_flinch* combat_flinch = new t_combat_flinch( attacker, defender, message, !flinch );

		add_attack( &attacker, &defender, false, message );

		if (attacker.has_ability( k_ability_breath_attack ))
			combat_flinch = check_breath( attacker, defender, combat_flinch, message );
		attack_handler = combat_flinch;
	}
	animation->set_key_frame_handler( attack_handler );
	// load defender flinch or die
	defender.preload_flinch( attacker );
}

// -----------------------------------------------------------------------
// handle end of a strike and return
// -----------------------------------------------------------------------
void t_battlefield::end_strike_and_return( t_combat_creature& creature, t_direction direction )
{
	set_current_direction( creature, direction );
	creature.set_wait_animation();
	update_state();
}

void t_battlefield::set_combat_window( t_combat_window* window )
{
	m_combat_window = window;
}


t_combat_window* t_battlefield::get_combat_window() const
{
	return m_combat_window;
}

// -----------------------------------------------------------------------
// handle end of a melee attack
// -----------------------------------------------------------------------
void t_battlefield::end_melee_attack( int attacks_left )
{
	t_combat_creature&   attacker = *m_acting_creature;
	t_attackable_object& defender = *m_target->get_attackable_object();

	if (defender.has_ability( k_ability_panic ) && m_defender_retaliated)
	{
		t_combat_creature* defender_creature;

		defender_creature = dynamic_cast<t_combat_creature*>( &defender );
		if (defender_creature != 0)
		{
			if (attacker.begin_panic( *defender_creature ))
			{
				attacker.clear_spell( k_spell_berserk, t_combat_action_message() );
				return;
			}
		}
	}
	if (attacks_left > 0 && attacker.is_awake() && defender.is_alive())
	{
		t_combat_creature_handler end_handler;

		add_action( m_resolve_handler );
		add_action( add_argument( bound_handler( *this, 
			                      &t_battlefield::end_melee_attack ), 
						          attacks_left - 1 ) );
		defender.check_blocking();
		begin_melee_attack( attacker, defender, true, false );
		return;
	}
	attacker.clear_spell( k_spell_berserk, t_combat_action_message() );
	if (attacker.has_ability( k_ability_panic ))
		defender.begin_panic( attacker );
	if (attacker.is_awake() 
		&& attacker.has_ability( k_ability_strike_and_return )
		&& m_current_cursor_mode != k_combat_mode_melee
		&& !attacker.is_active( k_spell_binding )
		&& m_starting_position != attacker.get_cell_position() )
	{
		t_combat_creature_handler handler;
		t_direction               direction;

		direction = get_direction( attacker, m_starting_position, *m_target );
		handler = add_2nd_argument( bound_handler( *this, 
			                        &t_battlefield::end_strike_and_return ), direction );
		m_data_cache.new_action(); // reset path finder
		begin_move( m_starting_position, handler );
	}
}

// -----------------------------------------------------------------------
// handle start of attack.
// -----------------------------------------------------------------------
void t_battlefield::begin_attack( t_combat_creature& attacker )
{
	assert( m_target->get_attackable_object() );
	assert( &attacker == m_acting_creature );

	t_attackable_object&      defender = *m_target->get_attackable_object();
	t_combat_creature_handler end_handler;
	int                       attacks = attacker.get_attacks( false );

	m_defender_retaliated = false;
	add_action( m_resolve_handler );
	defender.check_blocking();
	if (!defender.can_retaliate( attacker, false ))
	{
		add_action( add_argument( bound_handler( *this, 
			                      &t_battlefield::end_melee_attack ),
						          attacks - 1 ) );
		begin_melee_attack( attacker, defender, true, false );
		return;
	}

	bool	attacker_has_first_strike;
	bool    defender_has_first_strike;

	attacker.check_blocking();
	attacker_has_first_strike = attacker.has_ability( k_ability_first_strike )
		                        && !defender.has_ability( k_ability_first_strike_immunity );
	defender_has_first_strike = defender.has_ability( k_ability_first_strike )
		                        && !attacker.has_ability( k_ability_first_strike_immunity );
	if (!defender.is_awake() || attacker.is_blocking_attack() || defender.is_blocking_attack())
	{
		attacker_has_first_strike = true;
		defender_has_first_strike = false;
	}
	if (!attacker_has_first_strike && m_distance_moved < 4)
		defender_has_first_strike = false;

	t_combat_creature& enemy = dynamic_cast<t_combat_creature&>(*m_target);

	if (attacker_has_first_strike == defender_has_first_strike)
	{
		enemy.set_retaliated( !enemy.has_ability( k_ability_unlimited_retaliation ));

		add_action( add_argument( bound_handler( *this, 
			                      &t_battlefield::end_melee_attack ),
								  attacks - 1 ) );
		m_defender_retaliated = true;
		begin_melee_attack( attacker, enemy, false, false );
		begin_melee_attack( enemy, attacker, false, true );
		return;
	}

	if (attacker_has_first_strike)
	{
		add_action( bound_handler( *this, &t_battlefield::begin_melee_retaliation ) );
		begin_melee_attack( attacker, enemy, true, false );
		return;
	}
	m_defender_retaliated = true;
	enemy.set_retaliated( !enemy.has_ability( k_ability_unlimited_retaliation ));

	add_action( add_argument( bound_handler( *this, &t_battlefield::end_melee_attack ), 
		                      attacks ) );
	begin_melee_attack( enemy, attacker, true, true );
}

// -----------------------------------------------------------------------
// start a defender's retaliation
// -----------------------------------------------------------------------
void t_battlefield::begin_melee_retaliation()
{
	t_combat_creature&  attacker = *m_acting_creature;
	t_combat_creature&	defender = dynamic_cast<t_combat_creature&>(*m_target);
	int				    attacks  = attacker.get_attacks( false ) - 1;

	if (!defender.is_awake())
		return;

	m_defender_retaliated = true;
	defender.set_retaliated( !defender.has_ability( k_ability_unlimited_retaliation ));
	add_action( m_resolve_handler );
	add_action( add_argument( bound_handler( *this, &t_battlefield::end_melee_attack ), 
							  attacks ) );
	// do not check blocking for attacker, because it has already
	// been set in begin_attack()
	begin_melee_attack( defender, attacker, true, true );
}



// -----------------------------------------------------------------------
// handle end of walk action
// -----------------------------------------------------------------------
void t_battlefield::end_walk( t_combat_creature& creature )
{
	creature.set_wait_animation();
	update_state();
}

// -----------------------------------------------------------------------
// select next actor
// -----------------------------------------------------------------------
t_combat_creature* t_battlefield::select_next_creature( bool last_was_defender )
{
	t_combat_creature* new_actor = 0;
	t_combat_creature* actor;
	t_creature_list::iterator index;

	for (index = m_creature_list.begin(); index != m_creature_list.end(); index++)
	{
		actor = *index;
		if (actor->get_number() == 0)
			continue;
		if (actor->has_moved())
			continue;
		if (new_actor == 0 || actor->moves_before( *new_actor, last_was_defender ))
			new_actor = actor;
	}
	return new_actor;
}

// -----------------------------------------------------------------------
// pause, but continue processing message
// -----------------------------------------------------------------------
static void pause( t_uint32 delay )
{
	t_uint32 start_time = get_time();

	while (elapsed_time( start_time ) < delay )
		process_message();
}

// -----------------------------------------------------------------------
// check for good or bad morale and show it
// -----------------------------------------------------------------------
bool t_battlefield::show_good_morale()
{
	bool						shown = false;
	t_creature_list::iterator	index;
	t_combat_creature*			creature;

	for (index = m_creature_list.begin(); index != m_creature_list.end(); ++index)
	{
		creature = *index;
		if (creature->get_number() == 0 || creature->is_incapacitated()
			|| creature->has_moved())
			continue;
		if (creature->got_good_morale() && creature->show_morale_animation())
			shown = true;
	}
	return shown;
}

bool t_battlefield::show_bad_morale()
{
	bool						shown = false;
	t_creature_list::iterator	index;
	t_combat_creature*			creature;

	// check bad morale
	for (index = m_creature_list.begin(); index != m_creature_list.end(); ++index)
	{
		creature = *index;
		if (creature->get_number() == 0 || creature->is_incapacitated()
			|| creature->has_moved())
			continue;
		if (creature->show_morale_animation())
			shown = true;
	}
	return shown;
}

// -----------------------------------------------------------------------
// select next actor
// includes logic to reset movement for this turn.
// -----------------------------------------------------------------------
void t_battlefield::select_next_creature()
{
	t_combat_creature*			new_actor = 0;
	t_creature_list::iterator	index;
	bool						attackers_have_summoned_creatures;
	bool						defenders_have_summoned_creatures;
	bool						attackers_alive = false;
	bool						defenders_alive = false;

	assert( m_pending_actions.empty() );
	assert( m_attacks.empty() );
	if (m_acting_creature == 0) // if this is the very first action...
		initialize_morale(); 
	else
		update_morale();
	do
	{
		// check if one side is dead.
		attackers_alive = false;
		defenders_alive = false;
		attackers_have_summoned_creatures = false;
		defenders_have_summoned_creatures = false;
		for (index = m_creature_list.begin(); index != m_creature_list.end(); index++)
		{
			if ((*index)->get_number() <= 0)
				continue;
			if ((*index)->is_summoned())
			{
				if ((*index)->belongs_to_defender())
					defenders_have_summoned_creatures = true;
				else
					attackers_have_summoned_creatures = true;
			}
			else
			{
				if ((*index)->belongs_to_defender())
					defenders_alive = true;
				else
					attackers_alive = true;
			}
		}
		if (   (!attackers_alive || (!defenders_alive && !defenders_have_summoned_creatures))
			&& (!defenders_alive || (!attackers_alive && !attackers_have_summoned_creatures)))
		{
			if (m_fled[k_side_attacker])
				m_combat_window->set_result( k_result_attacker_escaped );
			else if (m_fled[k_side_defender])
				m_combat_window->set_result( k_result_defender_escaped );
			else if (!attackers_alive && !defenders_alive)
				m_combat_window->set_result( k_result_both_lost );
			else if (!attackers_alive)
				m_combat_window->set_result( k_result_attacker_lost );
			else
				m_combat_window->set_result( k_result_defender_lost );
			m_state = k_combat_state_finished;
			m_combat_window->display_creature_ready( 0 );
			set_buttons();
			pause( 3000 );
			end_combat();
			return;
		}

		new_actor = select_next_creature( m_last_was_defender );
		if (new_actor == 0)
		{

			m_turn_number++;
			for (index = m_creature_list.begin(); index != m_creature_list.end(); index++)
			{
				index->get()->process_new_turn();
			}
			new_actor = select_next_creature( m_last_was_defender );
		}
	} while (!set_acting( new_actor ));
}

// ------------------------------------------------------------------------
// get melee attack angle for location.
// ------------------------------------------------------------------------
bool t_battlefield::get_attack_angle( t_screen_point		screen_point, 
					                  t_attackable_object*	target,
									  t_attack_angle&		result ) const
{
	t_attack_angle_list const& angle_list = target->get_attack_angles( *this );

	if (angle_list.empty())
		return false;

	t_abstract_combat_object* object = target->get_object();
	t_map_point_3d target_center = object->get_footprint_center();
	t_screen_point base_point = object->get_screen_position();
	t_screen_point target_screen_point = subcell_to_screen_point( target_center );
	int            angle;

	angle = atan2( (double)((target_screen_point.y - screen_point.y) / 2),
		           (double)(target_screen_point.x - screen_point.x) ) * 180.0 / k_pi;
	angle += 45;

	// find closest attack angle
	t_attack_angle  new_angle;
	int             i;
	int				best = 500;
	int				difference;

	for (i = 0; i < angle_list.size(); i++)
	{
		new_angle = angle_list[i];
		difference = get_difference( angle, new_angle.attack_angle );
		if (difference < best)
		{
			result = new_angle;
			best = difference;
		}
	}
	return true;
}

// ------------------------------------------------------------------------
// get correct cursor for location.
// ------------------------------------------------------------------------
static t_external_string k_text_attack_creature( "attack.combat" );

t_mouse_window* t_battlefield::get_melee_cursor( t_screen_point screen_point, 
												 t_attackable_object* target,
												 std::string& help_text ) 
{
	t_attack_angle closest;

	if (!get_attack_angle( screen_point, target, closest ))
		return m_normal_cursor.cursor;

	int	cursor_angle;
	
	assert( closest.attack_angle >= 0 );
	cursor_angle = (closest.attack_angle * 4 + 45) / 90;
	cursor_angle = cursor_angle & 15;
	t_map_point_2d delta = target->get_object()->get_cell_position() - closest.point;

	t_wall_bonus bonus = crosses_wall( *m_acting_creature, *target );
	int movement;

	if ( closest.move_distance > m_acting_creature->get_combat_movement() )
		movement = 0; // Don't give champions multi-turn move bonuses
	else
		movement = closest.move_distance;

	int damage_low = m_acting_creature->get_total_damage_low( *target, false, movement, bonus );
	int damage_high = m_acting_creature->get_total_damage_high( *target, false, movement, bonus );

	help_text = replace_keywords( k_text_attack_creature,
								  "%creature_name", target->get_object_name(),
								  "%damage", damage_range_text( damage_low, damage_high ) );
	assert( m_melee_cursor[cursor_angle].frames != 0 );
	set_cursor_frame( closest.point, m_melee_cursor[cursor_angle] );
	return m_melee_cursor[cursor_angle].cursor;
}

// ------------------------------------------------------------------------
// check where a shot along a give line will land
// ------------------------------------------------------------------------
t_combat_creature* t_battlefield::get_ranged_target( t_combat_creature const& attacker,
												     t_map_point_2d const&    defender_center,
													 t_ranged_result&         result,
													 bool                     ignore_other_creatures) const
{
	// perform line of sight check
	t_map_point_2d				attacker_center = attacker.get_footprint_center();
	t_map_point_2d				position;
	t_map_point_2d				delta			  = defender_center - attacker_center;
	int							distance;
	t_combat_creature*			actor = 0;
	t_combat_creature*			last_target = 0;
	t_battlefield_cell const*	cell;
	t_stationary_combat_object* obstacle;
	int							attacker_edge_distance = 0;
	int							target_edge_distance = 0;
	int							wall_start_distance = 0;
	bool						wall_crossed = false;
	int							wall_distance = 0;
	int							total_distance;
	t_combat_creature*			target = 0;

	total_distance = abs( delta.row );
	if (total_distance < abs( delta.column ))
		total_distance = abs( delta.column );
	total_distance >>= k_battlefield_subcell_shift;
	distance = total_distance;
	result = k_ranged_result_normal;
	while (distance--)
	{
		position = defender_center - delta * distance / total_distance;
		position = position >> k_battlefield_subcell_shift;
		// check for an obstacle
		if (!is_valid( position ))
			continue;
		cell = &m_cell_map.get( position );
		if (cell->is_obscured())
		{
			obstacle = cell->get_obstacle();
			// special case: castle walls do not
			// obscure ranged attacks if the attack is travelling south
			// (row increment is positive).
			if (obstacle != 0
				&& (obstacle->get_obstacle_type() == k_obstacle_castle_wall
					|| (obstacle->get_obstacle_type() == k_obstacle_castle_gate && !gate_is_open() )))
			{
				if (!wall_crossed)
					wall_start_distance = distance;
				wall_distance = distance;
				wall_crossed = true;
			}
			else if (result == k_ranged_result_normal)
				result = k_ranged_result_obscured;
		}
		actor = cell->get_creature();
		if (actor == &attacker)
		{
			attacker_edge_distance = distance;
			continue;
		}

		// Pretend creatures (other than attacker & defender) aren't there if ignore_other_creatures
		if (   ignore_other_creatures 
			&& actor != 0
			&& (actor->get_footprint_center() != defender_center))
		{
			actor = 0;
		}

		if (actor == 0 || actor != last_target)
		{
			last_target = actor;
			target_edge_distance = distance;
		}
		if (actor != 0)
		{
			if (actor->get_controller() == attacker.get_controller()
				&& !attacker.is_active( k_spell_berserk )
				&& !actor->is_active( k_spell_berserk ))
				continue;
			if (actor->is_active( k_spell_sanctuary ))
				continue;
		}
		if (wall_crossed && !attacker.is_in_tower())
		{
			// check if missile crossed wall going inward
			if (delta.row < 0)
			{
				// if target is far enough beyond wall, it's blocked.
				if ( wall_start_distance - target_edge_distance > k_wall_safe_distance + 1)
					result = k_ranged_result_blocked;
			}
			else if (attacker_edge_distance - wall_distance > k_wall_safe_distance + 1)
			{
				// default for moving out of castle is NOT obscured.

				// if attacker is too far back from the wall and not in an arrow tower,
				// it cannot attack.
				result = k_ranged_result_blocked;
				continue;
			}
		}
		if (actor == 0)
			continue;
		if (target == 0 || actor->is_in_tower())
		{
			target = actor;
			if (actor->is_in_tower())
				result = k_ranged_result_obscured;
		}
	}
	if (wall_crossed && target == 0)
		result = k_ranged_result_blocked;
	return target;
}

// ------------------------------------------------------------------------
// check if a melee attack crosses a castle wall
// ------------------------------------------------------------------------
t_wall_bonus t_battlefield::crosses_wall( t_map_point_2d const& attacker_center,
										  t_map_point_2d const& defender_center ) const
{
	// perform line of sight check
	t_map_point_2d		  position        = attacker_center >> k_battlefield_subcell_shift;
	t_map_point_2d		  delta			  = defender_center - attacker_center;
	t_map_point_2d        sign_delta;
	t_map_point_2d		  sum;
	int					  threshold;
	int					  distance;
	t_battlefield_cell const*	cell;
	t_stationary_combat_object* obstacle;

	if (delta.row < 0)
		sign_delta.row = -1;
	else
		sign_delta.row = 1;
	if (delta.column < 0)
		sign_delta.column = -1;
	else
		sign_delta.column = 1;
	delta.row = abs( delta.row );
	delta.column = abs( delta.column );
	threshold = delta.row;
	if (threshold < delta.column)
		threshold = delta.column;
	distance = threshold >> k_battlefield_subcell_shift;
	// compute offset from "position" to real position in subcell units
	sum = attacker_center - (position << k_battlefield_subcell_shift);
	// convert this to 1/threshold units.
	sum = (sum * threshold) >> k_battlefield_subcell_shift;
	while (distance--)
	{
		sum += delta;
		if (sum.row >= threshold)
		{
			position.row += sign_delta.row;
			sum.row -= threshold;
		}
		if (sum.column >= threshold)
		{
			position.column += sign_delta.column;
			sum.column -= threshold;
		}
		// check for an obstacle
		if (!is_valid( position ))
			continue;
		cell = &m_cell_map.get( position );
		obstacle = cell->get_obstacle();
		if (obstacle == 0)
			continue;
		if (obstacle->get_obstacle_type() == k_obstacle_castle_wall)
			break;
		if (obstacle->get_obstacle_type() == k_obstacle_castle_gate && !gate_is_open())
			break;
	}
	if (distance < 0)
		return k_wall_bonus_none;
	if (sign_delta.row < 0)
		return k_wall_bonus_defense;
	return k_wall_bonus_attack;
}


// ------------------------------------------------------------------------
// check if a melee attack crosses a castle wall
// ------------------------------------------------------------------------
t_wall_bonus t_battlefield::crosses_wall( t_combat_creature const&	 attacker,
										  t_attackable_object const& defender ) const
{
	return crosses_wall( attacker.get_footprint_center(), 
						 defender.get_object()->get_footprint_center() );
}

// ------------------------------------------------------------------------
// check where a shot along a give line will land
// ------------------------------------------------------------------------
bool t_battlefield::can_see_subcell( t_combat_creature const& attacker, 
							         t_map_point_2d const& sub_cell) const
{
	t_ranged_result obscured;
	return can_see_subcell(attacker, sub_cell, obscured);
}

// ------------------------------------------------------------------------
// Check LOS to subcell
// ------------------------------------------------------------------------
bool t_battlefield::can_see_subcell( t_combat_creature const& attacker, 
							         t_map_point_2d const& sub_cell,
									 t_ranged_result &obscured) const
{
	assert( is_valid( sub_cell >> k_battlefield_subcell_shift ));
	t_combat_creature* result = get_ranged_target( attacker, sub_cell, obscured );

	if (obscured == k_ranged_result_blocked)
		return false;
	if (result == 0)
		return true;

	t_map_point_2d attacker_center = attacker.get_footprint_center();
	t_map_point_2d defender_center = result->get_footprint_center();
	t_map_point_2d vector_1		   = defender_center - attacker_center;
	t_map_point_2d vector_2        = sub_cell - defender_center;
	int			   dot_product;

	dot_product = vector_1.column * vector_2.column + vector_1.row * vector_2.row;
	return dot_product <= 0;
}

// ------------------------------------------------------------------------
// check where a shot along a give line will land
// ------------------------------------------------------------------------
bool t_battlefield::can_see_cell( t_combat_creature const& attacker, 
								  t_map_point_2d const& cell ) const
{
	t_ranged_result obscured;

	return can_see_subcell( attacker, 
							(cell << k_battlefield_subcell_shift) + k_battlefield_half_cell ,
							obscured);
}

// ------------------------------------------------------------------------
// Check LOS
// ------------------------------------------------------------------------
bool t_battlefield::can_see_cell( t_combat_creature const& attacker, 
								  t_map_point_2d const& cell,
								  t_ranged_result &obscured) const
{
	return can_see_subcell( attacker, 
							(cell << k_battlefield_subcell_shift) + k_battlefield_half_cell ,
							obscured);
}

// ------------------------------------------------------------------------
// check where a shot along a give line will land
// ------------------------------------------------------------------------
bool t_battlefield::can_see( t_combat_creature const& attacker,
							 t_combat_creature const& defender ) const
{
	t_ranged_result obscured;

	t_combat_creature* result = get_ranged_target( attacker, defender.get_footprint_center(),
		                                           obscured );

	return obscured != k_ranged_result_blocked && result == &defender;
}

// ------------------------------------------------------------------------
// check where a shot along a give line will land
// ------------------------------------------------------------------------
t_combat_creature* t_battlefield::get_ranged_target( t_combat_creature const& attacker,
												     t_combat_creature const& target ) const
{
	t_ranged_result obscured;

	t_combat_creature* result = get_ranged_target( attacker, target.get_footprint_center(),
		                                           obscured );
	if (result == 0)
		result = const_cast<t_combat_creature*>(&target);
	return result;
}

// ------------------------------------------------------------------------
// check if current creature can shoot
// ------------------------------------------------------------------------
bool t_battlefield::can_shoot( t_combat_creature const& creature ) const
{
	if (!creature.has_ability( k_ability_ranged ))
		return false;
	if (creature.is_active( k_spell_forgetfulness ))
		return false;
	if (creature.get_shots() <= 0)
		return false;
	if (enemy_adjacent( creature ))
		return false;
	return true;
}


// ------------------------------------------------------------------------
// check if current creature can shoot target
// ------------------------------------------------------------------------

int t_battlefield::get_range_factor( t_combat_creature const& attacker,
 									 t_combat_creature const& defender ) const
{
	if (!can_shoot( attacker ))
		return 0;
	if (defender.is_active( k_spell_sanctuary ))
		return 0;

	// add check for ZOC later.
	// perform line of sight check
	t_combat_creature* target;
	t_map_point_2d	   defender_center	  = defender.get_footprint_center();
	t_ranged_result	   obscured;

	target = get_ranged_target( attacker, defender.get_footprint_center(), obscured );
	if (obscured == k_ranged_result_blocked || target != &defender)
		return 0;
	
	int result = attacker.get_range_effect( attacker.get_edge_distance( defender ),
											obscured == k_ranged_result_obscured );

	return result;
}

// ------------------------------------------------------------------------
// get correct cursor for location.
// ------------------------------------------------------------------------
static t_external_string const k_text_throw_boulder( "throw_boulder.combat" );

t_mouse_window* t_battlefield::get_ranged_cursor( t_combat_creature const* target, int factor,
												  std::string& help_text )
{
	if (target == 0)
	{
		help_text = k_text_throw_boulder;
	}
	else
	{
		int damage_low = m_acting_creature->get_total_damage_low( *target, true, 0, k_wall_bonus_none );
		int damage_high = m_acting_creature->get_total_damage_high( *target, true, 0, k_wall_bonus_none );

		int factor = get_range_factor( *m_acting_creature, *target );

		if (factor > 1)
		{
			damage_low /= factor;
			damage_high /= factor;
		}

		if (damage_low < 1)
			damage_low = 1;

		if (damage_high < 1)
			damage_high = 1;


		help_text = replace_keywords( k_text_attack_creature, 
										"%creature_name", target->get_name(),
										"%damage", damage_range_text( damage_low, damage_high ) );
	}
	m_shoot_cursor.frames->set_frame( format_string( "%i", factor ) );
	return m_shoot_cursor.cursor;
}

// ------------------------------------------------------------------------
// get creature at a location
// ------------------------------------------------------------------------
t_combat_creature* t_battlefield::get_creature( t_map_point_2d const& point ) const
{
	return m_cell_map.get( point ).get_creature();
}

bool t_battlefield::get_area_range_target( t_combat_creature*	target,
										   t_map_point_2d&		map_point,
										   int					radius ) const
{
	t_combat_creature*	attacker = get_acting_creature();

	if (!can_see_cell( *attacker, map_point))
	{
		if (target == 0)
			return false;

		t_map_point_2d		footprint_center = attacker->get_footprint_center();
		t_map_point_2d		source_cell = footprint_center >> k_battlefield_subcell_shift;
		
		map_point = target->get_footprint_center();
		map_point >>= k_battlefield_subcell_shift;
		map_point = target->get_impact_point( source_cell, map_point );
		if (!can_see_cell( *attacker, map_point ))
			return false;
	}
	return true;
}


int t_battlefield::get_area_range_factor( t_combat_creature*	target, 
										  t_map_point_2d&		map_point,
										  int					radius ) const
{
	if (!get_area_range_target( target, map_point, radius ))
		return 0;

	t_combat_creature_list				targets;
	t_combat_creature_list::iterator	index;
	t_combat_creature*					attacker = get_acting_creature();

	targets = get_area_targets( get_cell_center(map_point), radius );
	index = targets.begin();

	// check if any enemies are in the blast radius
	for (; index != targets.end(); index++)
		if (!attacker->is_friendly( **index ))
			break;
	if (index == targets.end())
		return 0;
	return attacker->get_range_factor( map_point );
}


// ------------------------------------------------------------------------
// Return # of turns to reach point. -1 if point can't be reached, 4 if
// 4 or greater
// ------------------------------------------------------------------------
int t_battlefield::get_move_cursor_turns_to_point( t_combat_creature const & creature,
												   t_map_point_2d const & point ) const
{
	int movement = creature.get_combat_movement();
	int move_distance = 0;
	t_combat_path	path;
	int				i;
	int				turns = 0;

	if (creature.get_cell_position() == point)
	{
		return 1;
	}
	else 
	{
		if (!creature.get_path_finder().get_path( point, path ))
		{
			return -1;
		}

		// compute more accurate time than simple division, due to obstacles
		do
		{
			++turns;
			move_distance += movement;
			// find next waypoint (may be short if we're flying.)
			i = get_next_waypoint_index( path, move_distance );
			move_distance = path[i].move_cost;
		} while (i > 0 && turns < 4);
	}

	return turns;
}

// ------------------------------------------------------------------------
// set frame for distance
// ------------------------------------------------------------------------
void t_battlefield::set_cursor_frame( t_map_point_2d const& point, 
									  t_bitmap_cursor& cursor ) const
{
	int turns = get_move_cursor_turns_to_point( *m_acting_creature, point );

	if ( turns == -1 )
		return;
	
	cursor.set_frame( format_string( "%i", turns ) );
}

// ------------------------------------------------------------------------
// get move cursor
// ------------------------------------------------------------------------
static t_external_string const k_text_move_here( "move_here.misc" );
static t_external_string const k_text_fly_here( "fly_here.misc" );

t_mouse_window* t_battlefield::get_move_cursor( t_map_point_2d const& point,
											    std::string& help_text ) 
{
	t_bitmap_cursor*           cursor;

	select_target( 0 );
	if (m_acting_creature->is_flying())
	{
		help_text = k_text_fly_here;
		cursor = &m_fly_cursor;
	}
	else
	{
		help_text = k_text_move_here;
		cursor = &m_walk_cursor;
	}
	set_cursor_frame( point, *cursor );
	return cursor->cursor;
}

t_combat_spell* t_battlefield::get_default_spell() const
{
	return m_default_spell;
}

// ------------------------------------------------------------------------
// get correct cursor for location.
// ------------------------------------------------------------------------

t_mouse_window* t_battlefield::get_new_cursor( t_screen_point screen_point ) 
{
	t_mouse_window* result = m_normal_cursor.cursor;

	m_help_text = "";
	if (m_acting_creature == 0)
		return result;

	if (m_state != k_combat_state_awaiting_human_order)
		return result;

	t_combat_creature* actor = creature_hit_test( screen_point );

	if (actor != 0)
	{
		if (actor != m_last_target.get() 
			&& actor->get_current_action() == k_combat_actor_action_wait
			&& !actor->is_active( k_spell_frozen ))
		{
			actor->set_animation( k_combat_actor_action_fidget );
		}
		m_help_text = actor->get_name();
	}
	m_last_target = actor;

	if (m_spell != 0)
		return m_spell->mouse_move( screen_point, m_help_text );

	t_combat_user_action_ptr new_action;

	new_action = get_combat_user_action( *this, screen_point, actor );
	m_current_user_action = compare_user_actions( m_current_user_action, new_action );

	return m_current_user_action->get_cursor( *this, m_help_text );
}

// -----------------------------------------------------------------------
// handle a left button down
// -----------------------------------------------------------------------
void t_battlefield::left_button_down( t_screen_point const& screen_point )
{
	if (m_state != k_combat_state_awaiting_human_order)
		return;
	if (m_spell == 0)
		return;
	m_spell->left_button_down( screen_point );
}

// -----------------------------------------------------------------------
// handle a left click
// -----------------------------------------------------------------------
void t_battlefield::left_click( t_screen_point const& screen_point )
{
	if (m_state != k_combat_state_awaiting_human_order)
		return;
	if (m_acting_creature == 0)
		return;

	// If we have a spell in progress, do that
	if (m_spell != 0)
	{
		m_spell->left_click( screen_point );
	}
	else if ( m_current_user_action != NULL )
	{
		m_current_user_action->execute( *this );
	}

	if ( m_state != k_combat_state_awaiting_human_order )
		remove_movement_shading();
}

// -----------------------------------------------------------------------
// cast the default spell
// -----------------------------------------------------------------------
void t_battlefield::cast_default_spell( t_screen_point const& screen_point )
{
	t_spell spell = m_acting_creature->get_default_spell();

	if (spell == k_spell_none)
		return;

	m_spell = create_combat_spell( *this, spell );
	if (m_spell == 0)
		return;
	set_buttons();
	if (!m_spell->begin_casting() || !m_spell->left_click( screen_point ))
		m_spell = 0;
	set_buttons();
}



// ------------------------------------------------------------------------
// get angles target can be attacked from
// ------------------------------------------------------------------------
void t_battlefield::get_attack_angles( const t_attackable_object& target,
									   t_attack_angle_list      & result ) const
{
	get_attack_angles(*m_acting_creature, target, result);
}


// ------------------------------------------------------------------------
// get angles attacker can attack target  from
// ------------------------------------------------------------------------
void t_battlefield::get_attack_angles( const t_combat_creature  & attacker,
									   const t_attackable_object& target,
									   t_attack_angle_list      & result ) const
{
	result = m_data_cache.get_attack_angles(attacker, target);
}

// ------------------------------------------------------------------------
// add an actor to the threat map
// ------------------------------------------------------------------------
void t_battlefield::add_threat( t_combat_creature* actor )
{
	t_threat_footprint const& footprint = actor->get_threat_footprint();
	int                       footprint_size = footprint.get_size();
	t_map_point_2d            offset;
	t_map_point_2d            point = actor->get_cell_position();
	t_map_point_2d            new_point;
	int const*				  marked = footprint.get();
	t_direction const*        direction = footprint.get_angles();
	int                       actor_size = actor->get_footprint_size();
	int                       difference = (footprint_size - actor_size) / 2;

	point.row -= difference;
	point.column -= difference;
	for (offset.row = 0; offset.row < footprint_size; offset.row++)
	{
		for (offset.column = 0; offset.column < footprint_size; offset.column++)
		{
			if (*(marked++))
			{
				new_point = point + offset;
				if (m_cell_map.is_valid( new_point ))
				{
					m_cell_map.get( new_point ).add_threat( actor );
				}
			}
			direction++;
		}
	}
}

// ------------------------------------------------------------------------
// remove an actor to the threat map
// ------------------------------------------------------------------------
void t_battlefield::remove_threat( t_combat_creature* actor )
{
	t_threat_footprint const& footprint = actor->get_threat_footprint();
	int                       footprint_size = footprint.get_size();
	t_map_point_2d            offset;
	t_map_point_2d            point = actor->get_cell_position();
	t_map_point_2d            new_point;
	int const*				  marked = footprint.get();
	int                       actor_size = actor->get_footprint_size();
	int                       difference = (footprint_size - actor_size) / 2;

	point.row -= difference;
	point.column -= difference;
	for (offset.row = 0; offset.row < footprint_size; offset.row++)
	{
		for (offset.column = 0; offset.column < footprint_size; offset.column++)
		{
			if (*(marked++))
			{
				new_point = point + offset;
				if (m_cell_map.is_valid( new_point ))
				{
					m_cell_map.get( new_point ).remove_threat( actor );
				}
			}
		}
	}
}

// ------------------------------------------------------------------------
// check if any enemy creatures are adjacent to this one
// ------------------------------------------------------------------------
bool t_battlefield::enemy_adjacent( t_combat_creature const& actor, t_map_point_2d const& point ) const
{
	t_combat_creature_list::const_iterator index;
	t_combat_creature_list::const_iterator end = m_creature_list.end();
	t_combat_creature const*               creature;
	t_map_point_2d						   center;
	int									   half_footprint_size;
	int									   distance;
	int									   attack_distance;
	int									   angle;
	int									   facing;
	
	// creatures in arrow towers can always shoot.
	if (actor.is_in_tower())
		return false; 
	half_footprint_size = actor.get_half_footprint_size();
	center = point;
	center <<= k_battlefield_subcell_shift;
	center.row += half_footprint_size;
	center.column += actor.get_half_footprint_size();

	for (index = m_creature_list.begin(); index != end; index++)
	{
		creature = *index;
		if (creature->is_friendly( actor ) || !creature->is_awake())
			continue;

		distance = creature->get_edge_subcell_distance( center );
		attack_distance = (creature->get_attack_range() + 1) << k_battlefield_subcell_shift;
		if (distance > attack_distance + half_footprint_size)
			continue;

		// check angle
		angle = get_angle( creature->get_footprint_center(), center );
		facing = creature->get_current_direction() * 45;
		if ((get_difference( angle, facing ) << 1) <= creature->get_arc_threat())
			return true;
	}
	return false;
}

bool t_battlefield::enemy_adjacent( t_combat_creature const& actor ) const
{
	const t_combat_creature_list &list = m_data_cache.get_interferes_with_creature_list(actor);
	return !list.empty();
}

// ------------------------------------------------------------------------
// get angles for a given point where enemies impede an actor's movement.
// ------------------------------------------------------------------------
void t_battlefield::get_impeded_angles( t_combat_creature const& actor,
		                                t_map_point_2d const& point,
									    bool*                 angles ) const
{
	// determine which directions, if any, are across the grain
	// of an enemy's Zone of Control (aka Threat Area).
	t_combat_creature_list::const_iterator index;
	bool                                found_enemies = false;
	t_combat_footprint const&		    footprint = actor.get_footprint();
	int								    footprint_size = footprint.get_size();
	int                                 actor_size = actor.get_footprint_size();
	t_map_point_2d					    offset;
	t_map_point_2d					    new_point;
	t_combat_creature const*            enemy;
	int const*						    ptr = footprint.get();
	t_map_point_2d                      center;
	t_map_point_2d                      target_center;
	t_direction                         direction;
	bool                                flying = actor.is_flying();

	center = point << k_battlefield_subcell_shift;
	actor_size <<= k_battlefield_subcell_shift - 1;
	center.row += actor_size;
	center.column += actor_size;

	if (!actor.has_ability( k_ability_ignore_zones_of_control ))
	{
		// search each cell in the footprint
		for (offset.row = 0; offset.row < footprint_size; offset.row++)
		{
			for (offset.column = 0; offset.column < footprint_size; offset.column++)
			{
				if (!(*(ptr++)))
					continue;
				new_point = point + offset;

				t_cell              const&  cell        = m_cell_map.get( new_point );
				t_combat_creature_list const&  threat_list = cell.get_threat_list();

				// look at adjacent enemies
				for (index = threat_list.begin(); index != threat_list.end(); index++)
				{
					enemy = index->get();
					// skip actors on the same side
					if (enemy->get_controller() == actor.get_controller())
						continue;
					// skip dead actors
					if (enemy->get_number() == 0)
						continue;
					// skip non-flying actors if the subject flys.
					if (flying && !enemy->is_flying())
						continue;
					if (!found_enemies)
					{
						found_enemies = true;
						memset( angles, true, k_direction_count );
					}
					direction = get_direction( enemy->get_footprint_center(), center );
					direction = t_direction( direction & 3 );
					angles[direction] = false;
					angles[direction + 4] = false;
				}
			}
		}
	}
	if (!found_enemies)
		memset( angles, false, k_direction_count );
}


// ------------------------------------------------------------------------
// Handles a press of the auto-combat button
// ------------------------------------------------------------------------
void t_battlefield::set_auto_combat( bool local, bool val )
{
	assert( m_combat_window );

	bool set_attacker;
	bool set_defender;

	// Determine who is going to be affected
	set_attacker = local;
	set_defender = local;

	// Make the change to the local state
	if (set_attacker) {
		m_auto_combat_flags[k_side_attacker] = val;
	}
	if (set_defender) {
		m_auto_combat_flags[k_side_defender] = val;
	}
	set_buttons();

	// If we turned on auto combat, we may need to start the AI and announce it
	if (val) {
		assert( m_state == k_combat_state_awaiting_human_order );

		m_state = k_combat_state_choosing_action;
		choose_action();
	}
}

// ------------------------------------------------------------------------
// close a spellbook window
// ------------------------------------------------------------------------
void t_battlefield::cancel_spell()
{
	if (m_spell == 0 || m_state != k_combat_state_awaiting_human_order)
		return;
	m_spell = 0;
	set_buttons();
}

// ------------------------------------------------------------------------
// open a spellbook window
// ------------------------------------------------------------------------
static t_external_string const k_text_adventure_only( "adventure_only.spells" );

void t_battlefield::open_spellbook( t_window* parent )
{
	t_counted_ptr<t_spellbook_window> spellbook;
	t_spellbook_window_data           spell( k_spell_none );

	spellbook = new t_spellbook_window( m_acting_creature, parent, *( m_grail_data[ m_acting_creature->belongs_to_defender() ] ), t_spellbook_window::k_open_combat );
	if (!spellbook->run_modal())
		return;

	spell = spellbook->get_selected_spell();
	m_spell = create_combat_spell( *this, spell.spell );
	if (m_spell == 0)
	{
		std::string text = replace_keywords( k_text_adventure_only, "%spell_name",
											 get_spell_name( spell.spell ));
		ok_dialog( text, true );
		return;
	}
	if (spell.is_artifact)
		m_spell->set_artifact( spell.slot, spell.backpack_slot );
	set_buttons();
	if (!m_spell->begin_casting())
	{
		m_spell = 0;
		set_buttons();
	}

	// If mass or other "no-target" spell started, clear movement shadow
	if ( m_state != k_combat_state_awaiting_human_order )
		remove_movement_shading();
}

t_uint32 const k_defend_wait_message_delay = 1500; // In milliseconds

static t_external_string const k_combat_action_defend( "combat_action.defend" );
// ------------------------------------------------------------------------
// take Defend action
// ------------------------------------------------------------------------
void t_battlefield::defend()
{
	t_combat_action_message message( *m_acting_creature, k_combat_action_defend, k_combat_action_message_priority_wait_defend );
	m_defend_wait_displayer->display_action_message( message, k_defend_wait_message_delay );

	m_acting_creature->set_defending( true );
	m_state = k_combat_state_action_begun;
	update_state();
}

static t_external_string const k_combat_action_wait( "combat_action.wait" );
// ------------------------------------------------------------------------
// take the Wait action
// ------------------------------------------------------------------------
void t_battlefield::wait_action()
{
	t_combat_action_message message( *m_acting_creature, k_combat_action_wait, k_combat_action_message_priority_wait_defend );
	m_defend_wait_displayer->display_action_message( message, k_defend_wait_message_delay );

	m_acting_creature->set_waiting( true );
	m_state = k_combat_state_selecting_creature;
	select_next_creature();
}


// -----------------------------------------------------------------------
// reset wands
// -----------------------------------------------------------------------
static void reset_wands( t_combat_creature& creature )
{
	t_artifact_slot slot;
	t_artifact_effect_list::const_iterator index;
	t_artifact_effect_list::const_iterator end;
	t_artifact_prop::t_spell_charges*      charges;

	for (slot = t_artifact_slot(0); slot < k_artifact_slot_hero_count; enum_incr(slot))
	{
		t_artifact const&			  artifact = creature.get_artifact( slot );
		t_artifact_effect_list const& effects = artifact.get_effects();

		end = effects.end();
		for (index = effects.begin(); index != end; index++)
		{
			charges = dynamic_cast<t_artifact_prop::t_spell_charges*>(index->get());
			if (charges != 0)
				charges->reset_spell_points();
		}
	}
}

// -----------------------------------------------------------------------
// reset wands
// -----------------------------------------------------------------------
static void reset_wands( t_combat_creature_list& list )
{
	t_combat_creature_list::iterator index = list.begin();
	t_combat_creature_list::iterator end	= list.end();

	for (; index != end; index++)
	{
		if (!index->get()->is_summoned())
			reset_wands( **index );
	}
}

// -----------------------------------------------------------------------
// t_combat_window class
// handle end of combat
// -----------------------------------------------------------------------
extern t_external_string const k_text_resurrect_heroes;

void t_battlefield::end_combat()
{
	t_creature_list::iterator	index;
	t_combat_context&			context = *m_combat_window->get_context();
	t_creature_array*			losses = context.get_losses();
	t_player**					players = get_players();
	t_creature_stack_ptr		leaders[2];

	// save off the original leaders for use in combat results
	leaders[k_side_attacker] = &m_armies[k_side_attacker]->get_leader();
	leaders[k_side_defender] = &m_armies[k_side_defender]->get_leader();

	stop_music();
	reset_wands( m_creature_list );

	t_combat_creature*        creature;

	t_artifact_list booty; 	// Artifacts that have nowhere to go within the creature's army

	// count casualties.
	for (index = m_creature_list.begin(); index != m_creature_list.end(); index++)
	{
		creature = *index;
		creature->redistribute_artifacts( m_armies, booty );
		creature->record_losses( losses );
		creature->update_original_army( m_armies );
	}

	for (index = m_fled_stacks.begin(); index != m_fled_stacks.end(); index++)
	{
		creature = *index;
		creature->redistribute_artifacts( m_armies, booty );
		creature->record_losses( losses );
		creature->update_original_army( m_armies );
	}

	int	defender;
	for (defender = 0; defender < 2; defender++)
	{
		// consolidate neutrals
		if ( (players[ defender ] == 0) || players[ defender ]->is_computer() )
			m_armies[defender]->consolidate();
	}

	// Post process the results to deal with relocation, damage, cost, etc.
	t_combat_result			result = m_combat_window->get_result();

	post_process_combat_results(  m_armies, players, losses, result, 
							context.get_location(), m_combat_window, 
							context.get_town() );

	// Report the result to the human player(s)
	report_combat_results(  m_armies, players, losses, m_combat_window->get_result(),
							m_combat_window, context.get_town(), context.get_location(),
							leaders );

	// Shutdown the combat window
	t_battlefield_ptr ref = this;

	clear();
	m_combat_window->close();

	t_adventure_map& adventure_map = *context.get_adventure_map();
	t_adv_map_point	 location = context.get_location();

	post_process_apply_combat_results(	m_armies, players, losses, result, 
										context.get_location(), 
										context.get_adventure_frame(), 
										context.get_town() );

	run_result_scripts( context, losses, result );
	
	if (!adventure_map.is_game_over())
	{
		int i;

		for (defender = 0; defender < 2; ++defender)
			for (i = 0; i < t_creature_array::k_size; ++i)
				(*m_armies[defender])[i].clear_temporary_bonuses();

		t_army_ptr new_army_ptr;

		post_process_apply_combat_surrender(	m_armies, players, losses, result, 
												location, context.get_adventure_frame(), 
												context.get_town(),
												booty,
												context.get_are_real_armies(),
												new_army_ptr );

		m_combat_window->set_new_army_ptr( new_army_ptr );
	}
}




// -----------------------------------------------------------------------
// t_combat_window class
// check if creature is controlled by the computer
// -----------------------------------------------------------------------
bool t_battlefield::is_computer_controlled( bool defender ) const
{
	return m_players[defender] == 0 || m_players[defender]->is_computer() || m_players[defender]->is_eliminated();
}

bool t_battlefield::is_computer_controlled( t_combat_creature const& creature ) const
{
	if (creature.has_ability( k_ability_berserk ))
		return true;
	if (creature.is_active( k_spell_berserk ))
		return true;

	bool defender_controlled = creature.get_controller();

	if (m_auto_combat_flags[defender_controlled])
		return true;

	return is_computer_controlled( defender_controlled );
}

// -----------------------------------------------------------------------
// t_combat_window class
// handle an action by a neutral.  Very, very basic for now.
// -----------------------------------------------------------------------
void t_battlefield::get_berserk_targets( t_combat_creature const& attacker,
										 t_combat_creature_list&  targets ) const
{
	t_combat_creature_list::const_iterator	index;
	t_combat_creature*						target;
	int										distance;
	int										best_distance;
	t_attack_angle							attack_angle;

	for (index = m_creature_list.begin(); index != m_creature_list.end(); index++)
	{
		target = *index;
		if (target->get_number() == 0)
			continue;
		if (target == &attacker)
			continue;
		if (attacker.check_cowardice( *target ))
			continue;

		t_attack_angle_list const& attack_angles 
			= m_data_cache.get_attack_angles( attacker, *target );
		
		if (attack_angles.size() == 0)
			continue;

		attack_angle = *attack_angles.get_closest();
		distance = attack_angle.move_distance;
		if (!targets.empty())
		{
			if (best_distance < distance)
				continue;
			if (best_distance > distance)
				targets.clear();
		}
		targets.push_back( target );
		best_distance = distance;
	}
}


// -----------------------------------------------------------------------
// t_combat_window class
// handle an action by a neutral.  Very, very basic for now.
// -----------------------------------------------------------------------
void t_battlefield::choose_berserk_melee_action()
{
	t_combat_creature_list           targets;
	t_attack_angle                   attack_angle;

	get_berserk_targets( *m_acting_creature, targets );
	if (targets.empty())
	{
		if (!m_acting_creature->is_waiting())
			wait_action();
		else
			defend();
		return;
	}

	int								 total = targets.size();
	t_combat_creature_list::iterator index;

	total = random( total );
	for (index = targets.begin(); index != targets.end(); index++)
	{
		if (total-- == 0)
			break;
	}

	t_combat_creature* target = index->get();
	
  	t_attack_angle_list const& attack_angles = target->get_attack_angles( *this );
  
  	attack_angle = *attack_angles.get_closest();
  	move_to_attack( target, attack_angle.point );
}

// -----------------------------------------------------------------------
// t_combat_window class
// handle an action by a neutral.  Very, very basic for now.
// -----------------------------------------------------------------------
void t_battlefield::choose_berserk_ranged_action()
{
	t_combat_creature_list::iterator index;
	t_combat_creature*               target;
	t_combat_creature_list			 targets;
	int								 distance;
	int								 best_distance;

	for (index = m_creature_list.begin(); index != m_creature_list.end(); index++)
	{
		target = *index;
		if (target->get_number() == 0)
			continue;
		if (target == m_acting_creature.get())
			continue;
		if (m_acting_creature->check_cowardice( *target ))
			continue;
		if (target->is_active( k_spell_sanctuary ))
			continue;
		if (!can_see( *m_acting_creature, *target ))
			continue;
		distance = m_acting_creature->get_edge_distance( *target );
		if (!targets.empty())
		{
			if (best_distance < distance)
				continue;
			if (best_distance > distance)
				targets.clear();
		}
		targets.push_back( target );
		best_distance = distance;
	}
	if (targets.empty())
	{
		choose_berserk_melee_action();
		return;
	}

	int total = random( targets.size() );

	for (index = targets.begin(); index != targets.end(); index++)
	{
		if (total-- == 0)
			break;
	}
	target = index->get();
	begin_ranged_attack( target );
}


// -----------------------------------------------------------------------
// t_combat_window class
// Here is where we decide what to do with our turn.
// -----------------------------------------------------------------------
void t_battlefield::choose_action()
{
	if ( ( m_state != k_combat_state_choosing_action ) ||
		 m_running_animations > 0 )
		return;

	assert( m_acting_creature );
	assert( !m_acting_creature->has_moved() );

	remove_movement_shading();	


	// Update our state
	if (is_computer_controlled( *m_acting_creature )) {
		m_state = k_combat_state_awaiting_ai_order;
	} else {
		m_state = k_combat_state_awaiting_human_order;
		
		if ( get_show_combat_movement_shading() )
			set_movement_shading( *m_acting_creature );
	}


	// Update the UI
	set_buttons();


	// If the AI is going to handle this one, get it started
	if (m_state == k_combat_state_awaiting_ai_order)
	{

		// See what the AI wants/needs to do
		if (m_acting_creature->is_active( k_spell_berserk ))
		{
			if (can_shoot( *m_acting_creature ))
				choose_berserk_ranged_action();
			else
				choose_berserk_melee_action();
			return;
		}
		
		t_combat_ai ai(*m_acting_creature);
		
		if (ai_check_retreat( *this ))
			return;

		t_combat_ai_action_ptr action = ai.get_best_action();
		action->perform_action();

	}
}


// -----------------------------------------------------------------------
// t_combat_window class
// -----------------------------------------------------------------------
t_combat_creature* t_battlefield::get_acting_creature() const
{
	return m_acting_creature;
}

// -----------------------------------------------------------------------
// battlefield class
// -----------------------------------------------------------------------
t_mouse_window* t_battlefield::get_blocked_cursor() const
{
	return m_blocked_cursor.cursor;
}

t_mouse_window* t_battlefield::get_info_cursor() const
{
	return m_info_cursor.cursor;
}

// -----------------------------------------------------------------------
// battlefield class
// -----------------------------------------------------------------------
t_mouse_window* t_battlefield::get_normal_cursor() const
{
	return m_normal_cursor.cursor;
}

// -----------------------------------------------------------------------
// battlefield class
// -----------------------------------------------------------------------
t_mouse_window* t_battlefield::get_spell_cursor() const
{
	return m_spell_cursor;
}

t_mouse_window* t_battlefield::get_spell_cannot_cast_cursor() const
{
	return m_spell_cannot_cast_cursor.cursor;
}

t_mouse_window* t_battlefield::get_tower_move_cursor() const
{
	return m_tower_move_cursor.cursor;
}

// -----------------------------------------------------------------------
// battlefield class
// start spellcasting animation
// -----------------------------------------------------------------------
void t_battlefield::begin_throw_potion( t_direction direction )
{
	t_combat_creature_handler			   handler;
	t_spell								   spell;
	t_play_combat_animation_ptr			   animation;
	t_combat_action_message				   message( *m_acting_creature, m_spell->get_action_text( false ) );

	spell = get_potion_effect( m_spell->get_artifact_type() );
	start_animation( *m_acting_creature, spell, message );
	m_sequence_loader.add( m_acting_creature->get_cache( k_combat_actor_action_melee,
		                                                 direction ));
	handler = bound_handler( *m_spell, &t_combat_spell::execute );
	animation = new t_play_combat_animation( *m_acting_creature, direction,
		                                     k_combat_actor_action_melee );
	animation->set_key_frame_handler( handler );
	animation->set_end_handler( m_end_animation_handler );
	animation->display_action_message( message );
	m_acting_creature->set_animation( animation );
	begin_action();
}

// -----------------------------------------------------------------------
// battlefield class
// start spellcasting animation
// THIS IS THE CENTRAL WORKER FUNCTION.  All other begin_spell
// calls end up here.  Nobody else calls this directly
// -----------------------------------------------------------------------
void t_battlefield::begin_spell( t_direction					 direction,
								 t_combat_creature_handler		 handler,
								 t_combat_creature_handler		 end_handler,
								 t_combat_action_message const & message)
{
	m_sequence_loader.add( m_acting_creature->get_cache( k_combat_actor_action_cast_spell,
		                                                   direction ));

	t_play_combat_animation_ptr animation;

	animation = new t_play_combat_animation( *m_acting_creature, direction,
		                                     k_combat_actor_action_cast_spell );
	animation->set_key_frame_handler( handler );
	animation->set_end_handler( end_handler );
	animation->display_action_message( message );
	m_acting_creature->set_animation( animation );
}

// -----------------------------------------------------------------------
// get correct facing for an action
// -----------------------------------------------------------------------
static t_direction get_spell_direction( t_combat_creature const& caster,
									    t_map_point_2d const&    target )
{
	int angle;

	angle = get_angle( caster.get_footprint_center(), target );
	angle = ((angle + 45) / 90) << 1;
	return t_direction(angle & 7);
}

// -----------------------------------------------------------------------
// get correct facing for an action
// -----------------------------------------------------------------------
static t_direction get_spell_direction( t_combat_creature const* caster,
									    t_combat_creature const* target )
{
	if (target == 0 || target == caster)
		return caster->get_current_direction();
	return get_spell_direction( *caster, target->get_footprint_center() );
}

// -----------------------------------------------------------------------
// battlefield class
// start spellcasting animation
// SPECIAL CASE used only by mana leech pseudo-spell.  All other spells
// go through the two public routines below and use default handlers.
// -----------------------------------------------------------------------
void t_battlefield::begin_spell( t_combat_creature const* target, 
								 t_combat_creature_handler handler,
								 t_combat_creature_handler end_handler,
								 t_combat_action_message const & message )
{
	// If this is a direct result of human action, it would need to be sent to our
	// opponent, if any.  Only mana leeach uses this now, though, and the player
	// never does this directly.
	assert( m_state != k_combat_state_awaiting_human_order );

	begin_spell( get_spell_direction( m_acting_creature, target), handler, end_handler, message );
}

// -----------------------------------------------------------------------
// battlefield class
// start spellcasting animation
// -----------------------------------------------------------------------
void t_battlefield::begin_spell( t_map_point_2d const& target )
{
	assert( m_spell );

	if (m_state == k_combat_state_awaiting_human_order) {
	}

	t_combat_creature_handler handler;
	t_map_point_2d            position;

	position = get_cell_center( target );
	if (m_spell->is_potion())
	{
		begin_throw_potion( get_spell_direction( *m_acting_creature, position ) );
		return;
	}
	
	t_combat_action_message	message( *m_acting_creature, m_spell->get_action_text( false ) );

	handler = bound_handler( *m_spell, &t_combat_spell::execute );
	begin_spell( get_spell_direction( *m_acting_creature, position), handler,
		         m_end_animation_handler, message );
	begin_action();
}

// -----------------------------------------------------------------------
// battlefield class
// start spellcasting animation
// -----------------------------------------------------------------------
void t_battlefield::begin_spell( t_combat_creature const* target )
{
	assert( m_spell );

	if (m_state == k_combat_state_awaiting_human_order) {
	}

	t_combat_creature_handler handler;

	if (m_spell->is_potion())
	{
		if (target != 0 && target != m_acting_creature)
		{
			begin_throw_potion( get_spell_direction( m_acting_creature, target ) );
			return;
		}

		t_window_handler		handler;
		t_spell					spell;
		t_combat_action_message message( *m_acting_creature, m_spell->get_action_text( false ) );

		m_acting_creature->set_animation( k_combat_actor_action_fidget );
		spell = get_potion_effect( m_spell->get_artifact_type() );
		handler = discard_argument<t_window*>( 
				  add_argument<t_combat_creature&>( 
				  bound_handler( *m_spell, &t_combat_spell::execute ),
				  *m_acting_creature ) );
		start_animation( *m_acting_creature, spell, handler, message );
	}
	else
	{
		t_combat_action_message message( *m_acting_creature, m_spell->get_action_text( false ) );
		handler = bound_handler( *m_spell, &t_combat_spell::execute );
		begin_spell( get_spell_direction( m_acting_creature, target), handler, m_end_animation_handler, message );
	}


	begin_action();
}


// -------------------------------------------------------------------
// class to delay a spell animation until the previous one ends.
// -------------------------------------------------------------------
namespace
{
	class t_delayed_spell_animation : public t_handler_base_1<t_window*>, 
		                              public t_counted_animation
	{
	public:
		t_delayed_spell_animation( t_battlefield&		battlefield,
								   t_combat_creature&	creature, 
								   t_spell				spell,
								   t_window_handler		old_handler,
			                       t_window_handler		end_handler);
		virtual void operator()( t_window* window );

		virtual void display_action_message( t_combat_action_message const & message );
		virtual void erase_action_message();

	protected:
		t_combat_creature&			 m_creature;
		t_combat_action_message		 m_delayed_action_message;
		t_spell						 m_spell;
		t_window_handler			 m_old_handler;
		t_window_handler			 m_end_handler;
	};
};

// -------------------------------------------------------------------
// class to delay a spell animation until the previous one ends.
// -------------------------------------------------------------------
t_delayed_spell_animation::t_delayed_spell_animation( t_battlefield&	battlefield,
													  t_combat_creature& creature, 
													  t_spell spell, 
													  t_window_handler old_handler,
													  t_window_handler end_handler )
						 : t_counted_animation( battlefield ),
						   m_creature( creature ),
						   m_spell( spell ),
						   m_end_handler( end_handler ), m_old_handler( old_handler )
{
}

// -------------------------------------------------------------------
// class to delay a spell animation until the previous one ends.
// -------------------------------------------------------------------
void t_delayed_spell_animation::operator ()( t_window* window )
{
	t_counted_ptr<t_delayed_spell_animation> ref = this;

	m_old_handler( window );
	animation_ended();
	m_battlefield.start_animation( m_creature, m_spell, m_end_handler, m_delayed_action_message );
	m_old_handler = 0;
	m_end_handler = 0;
	m_delayed_action_message.clear();
}

// -----------------------------------------------------------------------
// Override and set the message to be displayed on the delayed animation
// -----------------------------------------------------------------------
void t_delayed_spell_animation::display_action_message( t_combat_action_message const & message )
{
	m_delayed_action_message = message;
}

// -----------------------------------------------------------------------
// Override and erase the message to be displayed on the delayed animation
// -----------------------------------------------------------------------
void t_delayed_spell_animation::erase_action_message()
{
	m_delayed_action_message.clear();
}

// -----------------------------------------------------------------------
// battlefield class
// start spell animation
// -----------------------------------------------------------------------
void t_battlefield::start_animation( t_combat_creature&				 creature, 
									 t_spell						 spell,
									 t_window_handler				 end_handler,
									 t_combat_action_message const & message)
{
	t_animation_ptr animation = get_spell_animation( spell, get_model_scale() );

	if (animation == 0)
	{
		m_spell = 0;
		end_handler( 0 );
		return;
	}

	t_spell_effect_window_ptr window;

	window = creature.get_spell_animation();
	if (window != 0)
	{
		t_window_handler old_handler = window->get_end_handler();

		t_delayed_spell_animation * delayed_animation = new t_delayed_spell_animation( *this, creature, spell,
																					   old_handler, end_handler);
		window->display_action_message( message );
		window->set_end_handler( delayed_animation );
		return; 
	}

	t_screen_point  origin;

	origin = creature.get_animation_position( animation );
	window = new t_spell_effect_window( &creature, animation, origin, get_combat_animation_speed(),
										m_viewer_ptr );
	window->set_end_handler( end_handler );
	window->display_action_message( message );
	window->set_range( 0, animation->get_frame_count(), false );
	get_combat_sound( spell )->play( get_sound_volume() );
	creature.set_spell_animation( window );
}


// -----------------------------------------------------------------------
// battlefield class
// start spell animation
// -----------------------------------------------------------------------
void t_battlefield::start_animation( t_combat_creature& creature, t_spell spell, t_combat_action_message const & message )
{
	t_window_handler   handler;

	handler = add_2nd_argument<t_combat_creature&>( bound_handler( *this, &t_battlefield::end_spell ),
												    creature );
	start_animation( creature, spell, handler, message );
}

// -----------------------------------------------------------------------
// battlefield class
// start spell animation
// -----------------------------------------------------------------------
void t_battlefield::start_pre_action_animation( t_combat_creature& creature, t_spell spell, t_combat_action_message const & message )
{
	t_window_handler   handler;

	assert( m_state == k_combat_state_selecting_creature );
	handler = add_2nd_argument<t_combat_creature&>( bound_handler( *this, &t_battlefield::end_pre_action_animation ),
		                                            creature );
	
	m_combat_window->enable_all_buttons( false );
	set_buttons();
	start_animation( creature, spell, handler, message );
}


// -----------------------------------------------------------------------
// battlefield class
// set influences for all creatures
// -----------------------------------------------------------------------
void t_battlefield::set_influences()
{
	t_combat_creature_iterator index = creatures_begin();
	t_combat_creature_iterator end   = creatures_end();

	for (; index != end; index++)
		index->get()->set_influence();
}

// -----------------------------------------------------------------------
// battlefield class
// set the passaiblity map for the battlefield.
// -----------------------------------------------------------------------
void t_battlefield::set_passability( t_battlefield_preset_map_in_game const * passability_map )
{
	
}

// -----------------------------------------------------------------------
// battlefield class
// add an action
// -----------------------------------------------------------------------
void t_battlefield::add_action( t_handler handler, bool to_front )
{
	if (to_front)
		m_pending_actions.push_front( handler );
	else
		m_pending_actions.push_back( handler );
}

static t_external_string const k_combat_action_magic_resistance( "combat_action.magic_resistance" );

// -----------------------------------------------------------------------
// battlefield class
// check a spell for resistance
// -----------------------------------------------------------------------
bool t_battlefield::check_resistance( t_combat_creature const& caster,
									  t_combat_creature&       target,
									  t_spell                  spell )
{
	if (!can_affect( caster, spell, target ))
		return false;
	if (can_resist( spell ) && random(100) >= caster.get_spell_chance( target, spell ))
	{
		t_combat_action_message message( target,
										 k_combat_action_magic_resistance,
										 k_combat_action_message_priority_magic_resistance );

		start_animation( target, k_spell_magic_resistance, message );
		return false;
	}
	return true;
}

// -----------------------------------------------------------------------
// battlefield class
// cast a spell
// -----------------------------------------------------------------------
bool t_battlefield::cast_spell( t_combat_creature const&		caster, 
							    t_combat_creature&				target,
								t_spell							spell,
								t_combat_action_message const & message,
								int								power )
{
	if (!check_resistance( caster, target, spell ))
		return false;
	spell = translate_spell( spell );
	target.set_spell( spell, message, power );
	start_animation( target, spell, message );
	return true;
}

// -----------------------------------------------------------------------
// battlefield class
// add an attack
// -----------------------------------------------------------------------
void t_battlefield::add_attack( t_combat_creature* attacker, t_attackable_object* defender,
					 		    bool ranged, t_combat_action_message const & message )
{
	int damage;

	if (ranged)
		damage = get_ranged_damage( *attacker, dynamic_cast<t_combat_creature&>(*defender) );
	else
	{
		t_wall_bonus bonus;
		int			 distance_moved = 0;

		if (attacker == m_acting_creature)
			distance_moved = m_distance_moved;
		bonus = crosses_wall( *attacker, *defender );
		damage = attacker->get_damage( *defender, ranged, distance_moved, bonus );
	}
	if (defender->is_blocking_attack())
		damage = 0;
	else
		damage = defender->add_damage( damage, attacker, true, false, message );
	if (!ranged)
		m_attacks.push_back( t_attack( *this, attacker, defender->get_object(), false, damage ));
	if (attacker->has_ability( k_ability_poison ))
	{
		t_combat_creature* defender_creature = dynamic_cast<t_combat_creature*>( defender );
		if ( defender_creature != 0
			 && can_affect( *attacker, k_spell_poison, *defender_creature ))
		{
			if (defender->has_ability( k_ability_death_protection ))
				damage >>= 1;
			defender->add_poison( damage >> 2, *attacker );
		}
	}
}

// -----------------------------------------------------------------------
// battlefield class
// resolve all attacks
// -----------------------------------------------------------------------
void t_battlefield::resolve_attacks()
{
	t_attack_list::iterator index;

	for (index = m_attacks.begin(); index != m_attacks.end(); index++)
	{
		index->resolve();
	}
	for (index = m_attacks.begin(); index != m_attacks.end(); index++)
	{
		index->check_vampirism();
	}
	m_attacks.clear();
}

// -----------------------------------------------------------------------
// battlefield class
// check morale
// -----------------------------------------------------------------------
void t_battlefield::initialize_morale()
{
	t_creature_list::iterator index = m_creature_list.begin();
	t_creature_list::iterator end   = m_creature_list.end();
	int                       i;
	
	for (i = 0; i < 2; i++)
	{
		m_original_hit_points[i] = 0;
		m_morale_modifier[i] = 0;
	}
	for (; index != end; index++)
	{
		index->get()->check_morale();
		m_original_hit_points[index->get()->belongs_to_defender()]
			+= index->get()->get_total_hits();
	}
}


// -----------------------------------------------------------------------
// battlefield class
// check morale
// -----------------------------------------------------------------------
void t_battlefield::update_morale()
{
	t_creature_list::iterator index = m_creature_list.begin();
	t_creature_list::iterator end   = m_creature_list.end();
	int                       i;
	int                       hit_points[2];
	int                       modifier[2];
	int                       new_modifier;
	int                       change;
	
	for (i = 0; i < 2; i++)
		hit_points[i] = 0;
	for (; index != end; index++)
	{
		hit_points[index->get()->belongs_to_defender()]
			+= index->get()->get_total_hits();
	}
	for (i = 0; i < 2; i++)
	{
		modifier[i] = 0;
		if (hit_points[i] < m_original_hit_points[i])
			modifier[i] = (hit_points[i] - m_original_hit_points[i]) * 10
				           / m_original_hit_points[i];
	}
	for (i = 0; i < 2; i++)
	{
		new_modifier = modifier[i] - modifier[1 - i];
		if (new_modifier > 0)
			new_modifier = 0;
		change = new_modifier - m_morale_modifier[i];
		m_morale_modifier[i] = new_modifier;
		if (change != 0)
		{
			for (index = m_creature_list.begin(); index != end; index++)
				if (index->get()->belongs_to_defender() == (i == 1))
					index->get()->change_morale( change );
		}
	}
}

// -----------------------------------------------------------------------
// set transparencey on an object based on what it obscures
// -----------------------------------------------------------------------
static void check_transparency( t_abstract_combat_object& object,
							    t_battlefield&			          battlefield,
 					    	    std::vector<t_screen_rect> const& selected_rects,
								std::vector<int>           const& depths )
{
	int			  new_alpha;

	new_alpha = object.get_normal_alpha();
	if (!object.is_underlay())
	{
		// if not, check if it obscures a selected creature
		t_screen_rect rect;
		int           object_depth;
		int			  i;

		rect = object.get_rect() + object.get_screen_position();
		rect.left -= 15;
		rect.top -= 15;
		rect.bottom += 15;
		rect.right += 15;
		object_depth = object.get_depth();
		for (i = selected_rects.size() - 1; i >= 0; i--)
		{
			if (rect.contains( selected_rects[i] ) && object_depth < depths[i])
				break;
		}
		if (i >= 0)
			new_alpha = 8;
		else
			new_alpha = object.get_normal_alpha();
	}
	if (object.is_fading())
		object.set_maximum_alpha( new_alpha );
	else
		object.set_alpha( battlefield, new_alpha );
}


// -----------------------------------------------------------------------
// battlefield class
// set transparency levels based on who is selected
// -----------------------------------------------------------------------
void t_battlefield::set_transparency()
{
	t_creature_list::const_iterator selected;
	t_creature_list::const_iterator selected_end = m_selected_creatures.end();
	t_creature_list::iterator       index = m_creature_list.begin();
	t_creature_list::iterator       end = m_creature_list.end();
	t_screen_rect                   rect;
	std::vector<t_screen_rect>      selected_rects;
	std::vector<int>                depths;

	// save all the rectangles which need to be visible
	for (selected = m_selected_creatures.begin(); selected != selected_end; selected++)
	{
		rect = selected->get()->get_rect() + selected->get()->get_screen_position();
		selected_rects.push_back( rect );
		depths.push_back( selected->get()->get_depth() );
	}
	// set alpha for every creature
	for (; index != end; index++)
		check_transparency( **index, *this, selected_rects, depths );

	t_stationary_combat_object_iterator wall = m_wall_objects.begin();
	t_stationary_combat_object_iterator wall_end = m_wall_objects.end();

	// set alpha for every wall object
	for (; wall != wall_end; wall++)
		check_transparency( **wall, *this, selected_rects, depths );
}

// -----------------------------------------------------------------------
// battlefield class
// set transparency levels based on who is selected
// -----------------------------------------------------------------------
void t_battlefield::select( t_combat_creature& creature, bool arg )
{
	if (arg == creature.is_selected())
		return;
	creature.set_selected( arg );
	if (arg)
	{
		m_selected_creatures.push_back( &creature );
		set_transparency();
		return;
	}

	t_creature_list::iterator index;
	t_creature_list::iterator end = m_selected_creatures.end();

	creature.set_selection_shadow( false );
	for (index = m_selected_creatures.begin(); index != end; index++)
		if (index->get() == &creature)
		{
			m_selected_creatures.erase( index );
			set_transparency();
			return;
		}
}

// -----------------------------------------------------------------------
// return player for a side
// -----------------------------------------------------------------------
t_player* t_battlefield::get_player( bool defender ) const
{
	return m_players[defender];
}

// -----------------------------------------------------------------------
// return proper player color for a side
// -----------------------------------------------------------------------
t_player_color t_battlefield::get_player_color( bool defender ) const
{
	t_player const* player = m_players[defender];

	if (player == 0)
		return k_player_gray;
	return player->get_color();
}

// -----------------------------------------------------------------------
// return a list of targets for an area effect attack
// -----------------------------------------------------------------------
t_combat_creature_list t_battlefield::get_area_targets( t_map_point_2d const& subcell, 
												        int radius ) const
{
	t_combat_creature_list			result;
	t_creature_list::const_iterator	index = m_creature_list.begin();
	t_creature_list::const_iterator	end = m_creature_list.end();
	t_combat_creature*				creature;

	for (; index != end; index++)
	{
		creature = *index;
		if (creature->get_number() <= 0)
			continue;
		if (creature->is_active( k_spell_sanctuary ))
			continue;
		if (is_in_castle( creature->get_cell_position() )
			&& !is_in_castle( subcell >> k_battlefield_subcell_shift ))
			continue;

		if (index->get()->get_edge_subcell_distance( subcell ) < radius)
			result.push_back( *index );
	}
	return result;
}

// -----------------------------------------------------------------------
// select new targets from a list
// -----------------------------------------------------------------------
void t_battlefield::select_targets( t_combat_creature_list const& targets )
{
	t_creature_list::iterator		index = m_selected_creatures.begin();
	t_creature_list::iterator       last;
	t_creature_list::iterator		end   = m_selected_creatures.end();
	t_creature_list::const_iterator creature;

	// remove old selection circles that no longer apply
	while (index != end)
	{
		last = index;
		index++;
		if (std::find( targets.begin(), targets.end(), *last ) == targets.end())
		{
			last->get()->set_selection_shadow( false );
			if (*last != m_acting_creature)
			{
				last->get()->set_selected( false );
				m_selected_creatures.erase( last );
			}
		}
	}
	// set new selection circles
	for (creature = targets.begin(); creature != targets.end(); creature++)
	{
		select( **creature, true );
		creature->get()->set_selection_shadow( true );
	}
	set_transparency();
}

// -----------------------------------------------------------------------
// select targets by radius
// -----------------------------------------------------------------------
void t_battlefield::select_targets( t_map_point_2d const& center, int radius )
{
	t_creature_list targets;

	targets = get_area_targets( get_cell_center(center), radius );
	select_targets( targets );
}

// -----------------------------------------------------------------------
// select the creature under the cursor
// -----------------------------------------------------------------------
void t_battlefield::select_target( t_combat_creature* creature )
{
	t_creature_list    targets;

	if (creature != 0)
		targets.push_back( creature );
	select_targets( targets );
}


// -----------------------------------------------------------------------
// get all creatures which have dead bodies in a cell
// -----------------------------------------------------------------------
t_combat_creature_list t_battlefield::get_resurrection_targets( t_screen_point const& point,
															    bool				  defender,
																t_spell				  spell ) const
{
	t_combat_creature_list    result;
	t_creature_list::iterator last;
	t_creature_list::iterator index;

	result = get_creatures_with_casualties( point, !defender, defender );
	index = result.begin();
	while (index != result.end())
	{
		last = index;
		index++;
		if (!can_place( **last, last->get()->get_cell_position() )
			|| !can_affect( *m_acting_creature, spell, **last ))
			result.erase( last );
	}
	return result;
}

// -----------------------------------------------------------------------
// get list of positions to place a creature
// -----------------------------------------------------------------------
t_map_point_2d_list t_battlefield::get_placement_points( t_map_point_2d const& center,
														 t_combat_creature&    creature,
														 int                   distance_variance)
{
	// find closest point to place this creature
	t_map_point_2d	    new_center;
	t_map_point_2d	    footprint_offset;
	t_map_point_2d	    point;
	t_map_point_2d_list points;
	int				    row_end;
	int				    distance;
	int				    best_distance;

	footprint_offset.column 
		= creature.get_footprint_size() << (k_battlefield_subcell_shift - 1);
	footprint_offset.row = footprint_offset.column;
	point.row = get_size();
	while (point.row--)
	{
		point.column = get_row_start( point.row );
		row_end = get_row_end( point.row );
		for (; point.column < row_end; point.column++)
		{
			if (!can_place( creature, point ))
				continue;
			new_center = point << k_battlefield_subcell_shift;
			new_center += footprint_offset;
			new_center -= center;
			distance = sqrt((double)(new_center.column * new_center.column 
				            + new_center.row * new_center.row));
			if (points.empty())
				best_distance = distance;
			else 
			{
				if (distance > best_distance + distance_variance)
					continue;
				if (distance < best_distance)
					best_distance = distance;
			}
			points.push_back( point );
		}
	}
	// trim points that are too far from best distance
	t_map_point_2d_list::iterator index;
	t_map_point_2d_list::iterator last;

	for (index = points.begin(); index != points.end(); )
	{
		last = index;
		index++;
		point = *last;
		new_center = point << k_battlefield_subcell_shift;
		new_center += footprint_offset;
		new_center -= center;
		distance = sqrt((double)(new_center.column * new_center.column 
				        + new_center.row * new_center.row));
		if (distance > best_distance + distance_variance)
		{
			points.erase( last );
			continue;
		}
	}
	return points;
}

// -----------------------------------------------------------------------
// get list of positions to place a creature
// -----------------------------------------------------------------------
t_combat_path_data const& t_battlefield::get_path_point( t_map_point_2d const& point ) const
{
	return get_current_path_finder().get_data( point );
}


// -----------------------------------------------------------------------
// place a simple or compound obstacle
// -----------------------------------------------------------------------
void t_battlefield::place_obstacle( t_combat_object_model_cache const&  cache,
		                            t_map_point_2d const&				position )
{
	if (cache.get()->get_segments().size() == 1)
	{
		t_stationary_combat_object* obstacle;

		obstacle = new t_stationary_combat_object( *this, cache );
		place_object( obstacle, position );
	}
	else
	{
		t_compound_object_ptr obstacle = new t_compound_object( cache );

		obstacle->place( *this, position );
	}
}

// -----------------------------------------------------------------------
// Find a nearby town
// -----------------------------------------------------------------------
t_town* t_battlefield::find_nearest_town( bool defender, 
										  bool & enemy_adjacent_towns_found,
										  bool & blocked_towns_found  ) const
{
	return m_combat_window->find_nearest_town( defender, enemy_adjacent_towns_found, 
											   blocked_towns_found );
}

// -----------------------------------------------------------------------
// use town portal
// -----------------------------------------------------------------------
void t_battlefield::use_town_gate()
{
	t_combat_action_message message( *m_acting_creature, get_short_spell_name( k_spell_town_gate ) );

	bool defender = m_acting_creature->belongs_to_defender();
	assert( !m_fled[ defender ] );

	t_creature_list::iterator creature_iter = m_creature_list.begin();
	while ( creature_iter != m_creature_list.end() )
	{
		t_creature_list::iterator next_creature = creature_iter;
		++next_creature;

		t_combat_creature & creature = **creature_iter;
		if ( creature.belongs_to_defender() == defender && !creature.is_summoned() )
		{
			m_fled_stacks.push_back( *creature_iter );
			t_combat_creature & creature = **creature_iter;
			creature.set_spell( k_spell_town_gate, message );
			creature.remove_from_map();
		}

		creature_iter = next_creature;
	}
	m_fled[ defender ] = true;

	update_state();
}

// -----------------------------------------------------------------------
// get list of creatures with casualties at a point
// -----------------------------------------------------------------------
t_combat_creature_list 
t_battlefield::get_creatures_with_casualties( t_screen_point const& point,
											  bool					attacker,
											  bool					defender ) const
{
	t_map_point_2d							map_point;
	bool									map_point_valid;
	t_combat_creature_list					result;
	t_creature_list::const_reverse_iterator end = m_creature_list.rend();
	t_creature_list::const_reverse_iterator index = m_creature_list.rbegin();
	t_uint32								current_time = get_time();
	t_combat_creature*						creature;

	map_point_valid = cell_hit_test( point, map_point );
	for ( ; index != end; ++index )
	{
		creature = index->get();
		if (creature->belongs_to_defender() && !defender)
			continue;
		if (!creature->belongs_to_defender() && !attacker)
			continue;
		if (creature->get_salvageable_bodies() <= 0)
			continue;
		if (creature->get_alpha() == 0)
			continue;
		if ( (map_point_valid && creature->in_footprint( map_point ))
			|| object_hit_test( *creature, current_time, point ) )
			result.push_back( creature );
	}
	return result;
}

// -----------------------------------------------------------------------
// get arrow tower within a footprint
// -----------------------------------------------------------------------
t_stationary_combat_object* t_battlefield::get_tower( t_map_point_2d const& point,
		                                              t_combat_footprint const& footprint ) const
{
	int				footprint_size = footprint.get_size();
	t_map_point_2d	offset;
	t_map_point_2d	new_point;
	int const*		ptr = footprint.get();
	t_stationary_combat_object* object;
	
	for (offset.row = 0; offset.row < footprint_size; offset.row++)
	{
		for (offset.column = 0; offset.column < footprint_size; offset.column++)
		{
			if (!(*(ptr++)))
				continue;
			new_point = point + offset;
			if (m_cell_map.is_valid( new_point ))
			{
				object = m_cell_map.get( new_point ).get_obstacle( k_obstacle_castle_tower );
				if (object != 0)
					return object;
			}
		}
	}
	return 0;
}

// -----------------------------------------------------------------------
// check if the gate area is blocked
// -----------------------------------------------------------------------
bool t_battlefield::gate_is_blocked() const
{
	if (m_gate == 0)
		return false;
	if (!gate_is_open()) // always allow gate to open.
		return false;

	t_map_rect_2d  rect;
	t_map_point_2d point;

	rect = m_gate->get_open_rect();
	for (point.row = rect.top(); point.row < rect.bottom(); ++point.row)
	{
		for (point.column = rect.left(); point.column < rect.right(); ++point.column)
		{
			if (m_cell_map.get( point ).get_creature() != 0)
				return true;
		}
	}
	return false;
}

// -----------------------------------------------------------------------
// check if the gate is open
// -----------------------------------------------------------------------
bool t_battlefield::gate_is_open() const
{
	if (m_gate == 0)
		return true;
	return m_gate->is_open();
}

// -----------------------------------------------------------------------
// open the castle gate
// -----------------------------------------------------------------------
void t_battlefield::mark_gate( bool arg )
{
	t_map_rect_2d  rect;
	t_map_point_2d point;

	rect = m_gate->get_open_rect();
	for (point.row = rect.top(); point.row < rect.bottom(); ++point.row)
	{
		for (point.column = rect.left(); point.column < rect.right(); ++point.column)
		{
			m_cell_map.get( point ).set_gate( arg );
		}
	}
}


// -----------------------------------------------------------------------
// open the castle gate
// -----------------------------------------------------------------------
void t_battlefield::open_gate( t_handler end_handler )
{
	assert( m_gate != 0 );
	m_gate->start_animation( *this, k_gate_animation_open, end_handler );
	mark_gate( true );
}


// -----------------------------------------------------------------------
// check if castle gate should be closed
// -----------------------------------------------------------------------
void t_battlefield::check_gate_close()
{
	if (m_gate == 0)
		return;
	if (!m_gate->is_open() || !m_gate->is_alive())
		return;
	if (gate_is_blocked())
		return;
	m_gate->start_animation( *this, k_gate_animation_close, t_handler() );
	mark_gate( false );
}

// -----------------------------------------------------------------------
// check if castle gate exists
// -----------------------------------------------------------------------
bool t_battlefield::has_gate() const
{
	if (m_gate == 0)
		return false;
	return (m_gate->is_alive());
}

// -----------------------------------------------------------------------
// return location of gate
// -----------------------------------------------------------------------
t_map_point_2d t_battlefield::get_gate_position() const
{
	return m_gate->get_position() >> k_battlefield_subcell_shift;
}

// -----------------------------------------------------------------------
// return location of gate
// -----------------------------------------------------------------------
t_map_rect_2d t_battlefield::get_gate_rect() const
{
	return m_gate->get_open_rect();
}

// -----------------------------------------------------------------------
// note addition of attackable obstacle
// -----------------------------------------------------------------------
void t_battlefield::added( t_attackable_obstacle* obstacle )
{
	m_attackable_obstacles.push_back( obstacle );
}

// -----------------------------------------------------------------------
// note removal of attackable obstacle
// -----------------------------------------------------------------------
void t_battlefield::removed( t_attackable_obstacle* obstacle )
{
	m_attackable_obstacles.remove( obstacle );
}

// -----------------------------------------------------------------------
// get timestamp for earning retailiations
// returns a value from -2,100,000 to 2,100,000
// -----------------------------------------------------------------------
int t_battlefield::get_retaliation_time_stamp() const
{
	return m_acting_creature->get_adjusted_speed();
}

// -----------------------------------------------------------------------
// update all creatures for retaliation
// -----------------------------------------------------------------------
void t_battlefield::update_retaliation()
{
	t_combat_creature_list::iterator index;
	t_combat_creature_list::iterator end;
	int								 time_stamp;

	time_stamp = get_retaliation_time_stamp();
	end = m_creature_list.end();
	for (index = m_creature_list.begin(); index != end; ++index)
	{
		index->get()->update_retaliation( time_stamp );
	}
}

// -----------------------------------------------------------------------
// get the appropriate army
// -----------------------------------------------------------------------
t_creature_array& t_battlefield::get_army( bool defender ) const
{
	return *m_armies[defender];
}

// -----------------------------------------------------------------------
// Get the grail data for the given side
// -----------------------------------------------------------------------
t_abstract_grail_data_source const & t_battlefield::get_grail_data( bool defender ) const
{
	return *m_grail_data[defender];
}


// -----------------------------------------------------------------------
// Provide a unique ID associated with this battlefield for anyone 
// who needs it (presently just t_abstract_combat_object)
// -----------------------------------------------------------------------
long t_battlefield::get_new_battlefield_id()
{
	return m_next_battlefield_id++;
}

int t_battlefield::get_active_creature_count( bool side ) const
{
	t_combat_creature_list::const_iterator	index = m_creature_list.begin();
	t_combat_creature_list::const_iterator	end = m_creature_list.end();
	t_combat_creature const*				creature;
	int										result = 0;

	for (; index != end; ++index)
	{
		creature = *index;
		if (creature->get_controller() != side)
			continue;
		if (creature->get_number() == 0)
			continue;
		if (creature->is_incapacitated())
			continue;
		++result;
	}
	return result;
}

// -----------------------------------------------------------------------
// find this first creature summoned by a particular spell on a side
// -----------------------------------------------------------------------
t_combat_creature* t_battlefield::find_summoned_creature( t_spell spell, 
														  t_creature_type creature_type, bool side )
{
	t_combat_creature_list::iterator	index = m_creature_list.begin();
	t_combat_creature_list::iterator	end = m_creature_list.end();
	t_combat_creature*					creature;
	t_spell								alternate_spell = spell;

	// little kludge to make these two spells equivalent
	if (spell == k_spell_illusionary_ally )
		alternate_spell = k_spell_clone;
	if (spell == k_spell_clone)
		alternate_spell = k_spell_illusionary_ally;

	for (; index != end; ++index)
	{
		creature = *index;
		if (creature->belongs_to_defender() != side)
			continue;
		if (!creature->is_summoned())
			continue;
		if (!creature->is_active( spell ) && !creature->is_active( alternate_spell ))
			continue;
		if (creature->get_hero() != 0)
			continue;
		if (creature->get_creature_type() != creature_type)
			continue;
		return creature;
	}
	return 0;
}

// -----------------------------------------------------------------------
// read saved game
// -----------------------------------------------------------------------
static bool read_cell_map( std::streambuf& stream, 
						  t_isometric_map<t_battlefield_cell>& cell_map,
						  int overall_version )
{
	t_map_point_2d point;
	int	i;	
	t_battlefield_cell*	cell;
	t_uint16					cell_version;

	if ( overall_version < 3 )
		cell_version = 0; // before we started keeping cell version numbers
	else
		cell_version = get< t_uint16 >( stream );

	int					size = get<t_int16>( stream );

	cell_map = t_isometric_map<t_battlefield_cell>( size, k_battlefield_cell_height, 
													k_battlefield_logical_view_size );

	for (point.row = 0; point.row < cell_map.get_size(); ++point.row)
	{
		point.column = cell_map.get_row_start( point.row );
		cell = &cell_map.get( point );
		i = cell_map.get_row_size( point.row );
		while (i--)
		{
			if (!cell->read( stream, cell_version ))
				return false;
			++cell;
		}
	}
	return true;
}

// -----------------------------------------------------------------------
// write saved game
// -----------------------------------------------------------------------
static bool write_cell_map( std::streambuf&								stream, 
						    t_isometric_map<t_battlefield_cell> const& cell_map )
{
	t_map_point_2d point;
	int	i;	
	t_battlefield_cell const* cell;

	put< t_uint16 >( stream, t_battlefield_cell::get_save_version() );
	put< t_int16 >( stream, cell_map.get_size() );
	for (point.row = 0; point.row < cell_map.get_size(); ++point.row)
	{
		point.column = cell_map.get_row_start( point.row );
		cell = &cell_map.get( point );
		i = cell_map.get_row_size( point.row );
		while (i--)
		{
			if (!cell->write( stream ))
				return false;
			++cell;
		}
	}
	return true;
}

// -----------------------------------------------------------------------
// read saved game
// -----------------------------------------------------------------------
static bool read_vertex_map( std::streambuf&									stream, 
							 t_isometric_vertex_map<t_battlefield_cell_vertex>& vertex_map)
{
	t_map_point_2d point;
	int	i;	
	t_battlefield_cell_vertex*  vertex;
	int							size;

	size = get<t_uint16>( stream );
	vertex_map = t_isometric_vertex_map<t_battlefield_cell_vertex>( size, 
																	k_battlefield_cell_height,
															k_battlefield_logical_view_size );

	for (point.row = 0; point.row < vertex_map.get_size(); ++point.row)
	{
		point.column = vertex_map.get_row_start( point.row );
		vertex = &vertex_map.get( point );
		i = vertex_map.get_row_size( point.row );
		while (i--)
		{
			vertex->set_height( get<t_int16>( stream ) );
			++vertex;
		}
	}
	return true;
}

// -----------------------------------------------------------------------
// write saved game
// -----------------------------------------------------------------------
static bool write_vertex_map( std::streambuf&											stream, 
						    t_isometric_vertex_map<t_battlefield_cell_vertex> const&	vertex_map )
{
	t_map_point_2d point;
	int	i;	
	t_battlefield_cell_vertex const* vertex;

	put<t_uint16>( stream, vertex_map.get_size() - 1 );
	for (point.row = 0; point.row < vertex_map.get_size(); ++point.row)
	{
		point.column = vertex_map.get_row_start( point.row );
		vertex = &vertex_map.get( point );
		i = vertex_map.get_row_size( point.row );
		while (i--)
		{
			put<t_int16>( stream, vertex->get_height() );
			++vertex;
		}
	}
	return true;
}

// -----------------------------------------------------------------------
// write saved game
// -----------------------------------------------------------------------

// -----------------------------------------------------------------------
// read saved game
// -----------------------------------------------------------------------
static int const k_current_version = 3;

bool t_battlefield::read( std::streambuf& stream )
{
	int version = ::get<t_uint16>( stream );

	if (version < 0 || version > k_current_version)
		return false;

	t_combat_reader reader( *this, stream );

	m_base_height = ::get<t_int16>( stream );
	m_castle_level = t_town_image_level( ::get<t_int8>( stream ) );
	m_castle_row = ::get<t_int16>( stream );
	if (version < 2)
		m_castle_inside_row = m_castle_row - 3;
	else
		m_castle_inside_row = get<t_uint16>( stream );
	if (!read_cell_map( stream, m_cell_map, version ))
		return false;
	if (!read_vertex_map( stream, m_cell_vertex_map ))
		return false;
	m_current_cursor_mode = t_combat_cursor_mode( ::get<t_int8>( stream ) );
	m_data_cache.new_action();
	m_data_cache.clear_paths();
	m_is_at_sea = ( ::get<t_int8>( stream ) != 0);
	m_last_was_defender = ( ::get<t_int8>( stream ) != 0 );
	::read( stream, m_morale_modifier, sizeof( m_morale_modifier ));
	if (version >= 1)
		::read( stream, m_retreat_checked, sizeof( m_retreat_checked ));
	else
		memset( m_retreat_checked, 0, sizeof( m_retreat_checked ));
	m_next_battlefield_id = ::get<t_int32>( stream );

	int	count;

	m_object_ptr_list.clear();
	count = ::get<t_int32>( stream );
	while (count--)
	{
		t_abstract_combat_object* object = reader.read_abstract_object();

		if ( !object )
			return false;

		object->place_during_read( *this );
	}
	m_acting_creature = reader.read_creature();

	count = ::get<t_uint32>( stream );
	while (count--)
	{
		t_attackable_obstacle* obstacle = reader.read_attackable_obstacle();

		m_attackable_obstacles.push_back( obstacle );
	}
	m_gate = reader.read_gate();
	::read( stream, m_original_hit_points, sizeof( m_original_hit_points ));
	assert( m_running_animations == 0 );

	count = ::get<t_int32>( stream );
	while (count--)
	{
		t_combat_creature* creature = reader.read_creature();

		m_selected_creatures.push_back( creature );
	}
	m_state = k_combat_state_selecting_creature;
	m_turn_number = ::get<t_uint32>( stream );

	count = ::get<t_int32>( stream );
	m_wall_objects.clear();
	while (count--)
	{
		t_stationary_combat_object* object = reader.read_stationary_object();

		m_wall_objects.push_back( object );
	}
	return true;
}

// -----------------------------------------------------------------------
// write saved game
// -----------------------------------------------------------------------
bool t_battlefield::write( std::streambuf& stream ) const
{
	t_combat_writer writer( *this, stream );

	put<t_uint16>( stream, k_current_version );


	put<t_int16>( stream, m_base_height );
	put<t_int8>( stream, m_castle_level );
	put<t_int16>( stream, m_castle_row );
	put<t_int16>( stream, m_castle_inside_row );
	if (!write_cell_map( stream, m_cell_map ))
		return false;
	if (!write_vertex_map( stream, m_cell_vertex_map ))
		return false;
	put<t_int8>( stream, m_current_cursor_mode );
	put<t_int8>( stream, m_is_at_sea );
	put<t_int8>( stream, m_last_was_defender );
	::write( stream, m_morale_modifier, sizeof( m_morale_modifier ));
	::write( stream, m_retreat_checked, sizeof( m_retreat_checked ));
	put<t_int32>( stream, m_next_battlefield_id );

	t_object_ptr_list::const_iterator object_iterator;
	t_object_ptr_list::const_iterator objects_end = m_object_ptr_list.end();

	t_int32 object_count = m_object_ptr_list.size();

	// Don't count unsaved objects
	for ( object_iterator = m_object_ptr_list.begin(); object_iterator != objects_end; ++object_iterator)
	{
		if ( (*object_iterator)->get_object_type() == k_combat_object_type_unsaved )
			object_count--;
	}

	put<t_int32>( stream, object_count );
	for ( object_iterator = m_object_ptr_list.begin(); object_iterator != objects_end; ++object_iterator)
	{
		if ( (*object_iterator)->get_object_type() != k_combat_object_type_unsaved )
			writer.write( object_iterator->get() );
	}
	writer.write( m_acting_creature );

	t_attackable_obstacle_list::const_iterator attackable_obstacle = m_attackable_obstacles.begin();

	put<t_uint32>( stream, m_attackable_obstacles.size() );
	for (; attackable_obstacle != m_attackable_obstacles.end(); ++attackable_obstacle)
	{
		writer.write( *attackable_obstacle );
	}
	writer.write( m_gate );
	::write( stream, m_original_hit_points, sizeof( m_original_hit_points ));
	assert( m_running_animations == 0 );

	t_combat_creature_list::const_iterator creature = m_selected_creatures.begin();

	put<t_uint32>( stream, m_selected_creatures.size() );
	for (; creature != m_selected_creatures.end(); ++creature)
	{
		writer.write( *creature );
	}
	assert( m_state == k_combat_state_awaiting_human_order );
	put<t_uint32>( stream, m_turn_number );

	put<t_uint32>( stream, m_wall_objects.size() );

	t_stationary_combat_object_list::const_iterator wall_iterator;

	for (wall_iterator = m_wall_objects.begin(); wall_iterator != m_wall_objects.end();
	     ++wall_iterator )
	{
		writer.write( *wall_iterator );
	}
	return true;
}

bool t_battlefield::get_acting_side() const
{
	return m_acting_creature->get_controller();
}

// -----------------------------------------------------------------------
// calculate the cost in gold for a player to surrender
// -----------------------------------------------------------------------
int t_battlefield::calculate_surrender_cost( bool side ) const
{
	int										cost = 0;
	t_skill_mastery							charm = k_mastery_none;
	t_skill_mastery							diplomacy = k_mastery_none;
	t_combat_creature_list::const_iterator	index = m_creature_list.begin();
	t_combat_creature_list::const_iterator	end = m_creature_list.end();
	t_combat_creature const*				creature;
	t_hero const*							hero;

	for (; index != end; ++index)
	{
		creature = *index;
		if (creature->is_summoned())
			continue;
		// pay for heroes even if they're dead.
		if (creature->get_number() == 0 && creature->get_hero() == 0)
			continue;
		if (creature->belongs_to_defender() != side)
			continue;
		hero = creature->get_hero();

		if (hero == 0) // it's a creature, not a hero
		{
			t_creature_stack* creature_stack = creature->get_creature_stack();
			t_creature_traits const& traits = creature_stack->get_creature()->get_traits();

			cost += creature->get_number() * traits.cost[k_gold];
		}
		else
		{
			cost += hero->get_resurrection_cost();
			// look for the highest charm skill among the heroes
			if (hero->get_skill(k_skill_charm) > charm)
				charm = hero->get_skill(k_skill_charm);

			// look for the highest diplomacy skill among the heroes
			if (hero->get_skill(k_skill_diplomacy) > diplomacy)
				diplomacy = hero->get_skill(k_skill_diplomacy);
		}
	}

	float cost_percentage = get_charm_surrender_percentage(charm);
	
	if (cost_percentage > get_diplomacy_surrender_percentage(diplomacy))
		cost_percentage = get_diplomacy_surrender_percentage(diplomacy);

	// final cost
	cost = cost * cost_percentage / 100.0;

	return cost;
}

// -----------------------------------------------------------------------
// execute the actual surrender
// -----------------------------------------------------------------------
void t_battlefield::surrender()
{
	bool		side = get_acting_side();
	int			cost = calculate_surrender_cost( side );
	
	get_player( side )->spend( k_gold, cost );
	get_player( !side )->gain( k_gold, cost );

	if (side)
		m_combat_window->force_combat_termination(k_result_defender_surrendered);
	else
		m_combat_window->force_combat_termination(k_result_attacker_surrendered);
}


static t_external_string const k_text_shackles_prevent_retreat( "shackles_prevent_retreat.misc" );

bool t_battlefield::shackles_of_war_present( std::string* text ) const
{
	t_combat_creature_list::const_iterator	index = m_creature_list.begin();
	t_combat_creature_list::const_iterator	end	= m_creature_list.end();
	t_combat_creature*						creature;
	t_hero*									hero;

	for (; index != end; ++index)
	{
		creature = *index;
		if (creature->get_number() == 0)
			continue;
		hero = creature->get_hero();
		if (hero == 0)
			continue;

		t_artifact* artifact;

		if (has_shackles_of_war( *hero, artifact ))
		{
			if (text != 0)
			{
				t_hero_keyword_replacer replacer;

				replacer.add_hero( hero );
				replacer.add_artifact( *artifact );
				*text = replacer( k_text_shackles_prevent_retreat );
			}
			return true;
		}
	}
	return false;
}

// -----------------------------------------------------------------------
// check if retreat is possible
// -----------------------------------------------------------------------
static t_external_string const k_no_town_after_retreat( "no_town_after_retreat.combat");
static t_external_string const k_text_siege_blocks_retreat( "no_retreat_from_town.combat" );
static t_external_string const k_town_blocked_by_enemy_retreat( "town_blocked_by_enemy_retreat.combat" );
static t_external_string const k_town_blocked_retreat( "town_blocked_retreat.combat" );
static t_external_string const k_wish_to_retreat( "wish_to_retreat.combat" );
static t_external_string const k_text_concede( "concede.combat" );
static t_external_string const k_text_no_heroes( "no_heroes.misc" );
static t_external_string const k_text_town_too_close( "town_too_close.combat" );

bool t_battlefield::can_retreat( bool is_defender, std::string* text ) const
{
	t_combat_context*							context = m_combat_window->get_context();
	t_ownable_garrisonable_adv_object const*	garrison = context->get_garrison();

	// No-remove garrisons do not retreat or surrender
	if ( is_defender && garrison && !garrison->can_remove_garrison() )
	{
		if (text != 0)
			*text = garrison->get_text( "retreat_override" );
		return false;
	}
	if (is_defender && context->get_town() != 0)
	{
		if (text != 0)
			*text = k_text_siege_blocks_retreat;
		return false;
	}
	if (shackles_of_war_present( text ))
		return false;
	// check for heroes.
	t_combat_creature_list::const_iterator	index = m_creature_list.begin();
	t_combat_creature_list::const_iterator	end	= m_creature_list.end();
	t_combat_creature*						creature;

	for (; index != end; ++index)
	{
		creature = *index;
		if (creature->get_number() == 0)
			continue;
		if (creature->belongs_to_defender() != is_defender)
			continue;
		if (creature->get_hero() != 0)
			break;
	}
	if (index == end)
	{
		if (text != 0)
			*text = k_text_no_heroes;
		return false;
	}

	t_town*	town_ptr;
	bool	enemy_adjacent_towns_found;
	bool	blocked_towns_found;
	bool	nearby_town_found;

	town_ptr = m_combat_window->find_nearest_town( is_defender, enemy_adjacent_towns_found, 
												   blocked_towns_found, 0, 
												   &nearby_town_found );
	if ( town_ptr )
	{
		if (text != 0)
			*text = replace_keywords( k_wish_to_retreat, "%town_name", town_ptr->get_name() );
		return true;
	}

	if ( enemy_adjacent_towns_found )
	{
		if (text != 0)
			*text = k_town_blocked_by_enemy_retreat;
		return false;
	}
	if ( blocked_towns_found )
	{
		if (text != 0)
			*text = k_town_blocked_retreat;
		return false;
	}
	if (nearby_town_found)
	{
		if (text != 0)
			*text = k_text_town_too_close;
		return false;
	}

	// you must own a town to retreat to
	if (text != 0)
		*text = k_no_town_after_retreat;
	
	return false;
}

// -----------------------------------------------------------------------
// check if surrender is possible
// -----------------------------------------------------------------------
static t_external_string const k_no_surrender_to_neutral( "no_surrender_to_neutral.misc" );
static t_external_string const k_no_town_after_surrender( "no_town_after_surrender.combat" );
static t_external_string const k_town_blocked_by_enemy_surrender( "town_blocked_by_enemy_surrender.combat" );
static t_external_string const k_town_blocked_surrender( "town_blocked_surrender.combat" );
static t_external_string const k_wish_to_surrender( "wish_to_surrender.combat" );
static t_external_string const k_not_enough_gold_to_surrender( "insufficient_gold.combat" );


bool t_battlefield::can_surrender( bool is_defender, std::string* text ) const
{
	t_combat_context*							context = m_combat_window->get_context();
	t_ownable_garrisonable_adv_object const*	garrison = context->get_garrison();

	// No-remove garrisons also do not retreat or surrender
	if ( is_defender && garrison && !garrison->can_remove_garrison() )
	{
		if (text != 0)
			*text = garrison->get_text( "surrender_override" );
		return false;
	}

	if (get_player_color( !is_defender ) == k_player_gray ) 
	{
		// cannot surrender to neutral army
		if (text != 0)
			*text = k_no_surrender_to_neutral;
		return false;	// return to combat
	}

	if (shackles_of_war_present( text ))
		return false;

	t_town*	town_ptr;
	bool enemy_adjacent_towns_found;
	bool blocked_towns_found ;
	bool nearby_town_found;

	town_ptr = m_combat_window->find_nearest_town( is_defender, enemy_adjacent_towns_found, 
												   blocked_towns_found, 0, &nearby_town_found);

	if ( NULL == town_ptr )
	{
		if ( enemy_adjacent_towns_found )
		{
			if (text != 0)
				*text = k_town_blocked_by_enemy_surrender;
			return false;
		}
		if ( blocked_towns_found )
		{
			if (text != 0)
				*text = k_town_blocked_surrender;
			return false;
		}
		if (nearby_town_found)
		{
			if (text != 0)
				*text = k_text_town_too_close;
			return false;
		}
		if (text != 0)
			// you must own a town to retreat to
			*text = k_no_town_after_surrender;
		return false;
	}

	t_player*	player = get_player(is_defender);
	int			cost = calculate_surrender_cost( is_defender );

	t_material_array const & funds = player->get_funds();

	if ( cost > funds[k_gold] )
	{
		// player lacks the funds to surrender
		if (text != 0)
			*text = k_not_enough_gold_to_surrender;
		return false;	// return to combat
	}

	if (text != 0)
	{
		std::string cost_text = format_string( "%i", cost );

		*text = replace_keywords( k_wish_to_surrender, 
								  "%surrender_cost", cost_text,
								  "%town_name", town_ptr->get_name() );
	}
	return true;
}

// -----------------------------------------------------------------------
// check if a point is within the castle
// -----------------------------------------------------------------------
bool t_battlefield::is_in_castle( t_map_point_2d const& point ) const
{
	if (m_castle_level == k_town_image_village)
		return false;
	return point.row < m_castle_inside_row;
}

// -----------------------------------------------------------------------
// get thinnest piece of castle wall
// -----------------------------------------------------------------------
int t_battlefield::get_wall_thickness() const
{
	t_stationary_combat_object_list::const_iterator index = m_wall_objects.begin();
	t_stationary_combat_object_list::const_iterator end = m_wall_objects.end();
	t_stationary_combat_object const*				object;
	t_compound_object const*						compound_object;
	int												thickness;
	int												thinnest;
	bool											found = false;

	for (; index != end; ++index)
	{
		object = *index;
		if (object->get_obstacle_type() != k_obstacle_castle_wall)
			continue;
		compound_object = object->get_compound_object();
		if (compound_object != 0)
			thickness = compound_object->get_cell_rect().height();
		else
			thickness = object->get_cell_rect().height();
		if (found && thickness > thinnest)
			continue;
		found = true;
		thinnest = thickness;
	}
	return thinnest;
}

void t_battlefield::remove_gate( t_stationary_combat_object* object )
{
	t_stationary_combat_object_ptr object_ptr = object;

	m_wall_objects.remove( object_ptr );
}

void t_battlefield::add_gate( t_stationary_combat_object* object )
{
	m_wall_objects.push_back( object );
}

double t_battlefield::get_castle_wall_bonus() const
{
	t_town_image_level	castle_level = get_castle_level();
	static double const k_wall_bonus_value[k_town_image_count] = { 1.0, 1.25, 1.50, 2.0 };

	return k_wall_bonus_value[castle_level];
}
	
t_difficulty t_battlefield::get_difficulty() const
{
	t_combat_context const& context = *m_combat_window->get_context();
	t_adventure_map const&	map = *context.get_adventure_map();

	return map.get_player_difficulty();
}

// Stop shading the area a creature can move to in one turn
void t_battlefield::remove_movement_shading()
{
	if ( m_movement_shader.get() != NULL )
		m_movement_shader->reset( *this );

	m_current_movement_shaded_creature = NULL;

	m_movement_shading_resetter.cancel();
}

// Start shading the area the given creature can move to in one turn
void t_battlefield::set_movement_shading( t_combat_creature const& center )
{
	if ( m_movement_shader.get() == NULL )
	{
		m_movement_shader = new t_combat_area_shader( *this );
		place_object( m_movement_shader.get(), t_map_point_3d( 0, 0, 0 ) );
	}

	m_current_movement_shaded_creature = &center;

	t_combat_path_finder&		path_finder = center.get_path_finder();

	int size = get_size();
	t_combat_area_shader_data data( size );

	t_uint8 color_index = data.add_to_palette( convert_to_16_bit( 50, 50, 200 ) );
	t_map_point_2d point;

	for ( point.row = 0; point.row < size; point.row++ )
	{
		for ( point.column = 0; point.column < size; point.column++ )
		{
			if ( is_valid( point ) )
			{
				t_combat_path_data const*	path_point = &path_finder.get_data( point );
				
				if ( path_point->reachable )
				{
					t_map_point_2d nearest_point = path_point->nearest_point;
					path_point = &path_finder.get_data( nearest_point );
				
					if (path_point->move_cost > 0 && get_move_cursor_turns_to_point( center, nearest_point ) == 1 )
					{
						data.set_tile_to_color( point, color_index );
					}
				}
			}
		}
	}

	m_movement_shader->set_shading( data, *this );

	m_movement_shading_resetter.cancel();
}

// -----------------------------------------------------------------------
// Get the creature which should currently have movement shading around 
// it.
// -----------------------------------------------------------------------
t_combat_creature const * t_battlefield::get_current_movement_shaded_creature() const
{
	// If the creature has been explicitly set, use that creature
	if ( m_current_movement_shaded_creature != NULL )
		return m_current_movement_shaded_creature;

	// Otherwise, use currently acting creature, if any
	return m_acting_creature;
}


// -----------------------------------------------------------------------
// Set the movement shading to the default (off if the user has it off, 
// the active creature if the user has it on)
// -----------------------------------------------------------------------
void t_battlefield::set_movement_shading_to_default()
{
	if ( get_show_combat_movement_shading() && 
		 m_state == k_combat_state_awaiting_human_order )
	{
		set_movement_shading( *m_acting_creature );
	}
	else
	{
		remove_movement_shading();
	}
}
